2020-03-09 18:35:20 +01:00
|
|
|
//go:generate go run bundler.go
|
|
|
|
|
2020-02-27 19:04:34 +01:00
|
|
|
// Copyright 2016-2020, Pulumi Corporation.
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
// nolint: lll, goconst
|
|
|
|
package docs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
2020-03-09 18:35:20 +01:00
|
|
|
"html"
|
|
|
|
"html/template"
|
2020-02-27 19:04:34 +01:00
|
|
|
"path"
|
2020-04-21 13:28:44 +02:00
|
|
|
"regexp"
|
2020-02-27 19:04:34 +01:00
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
|
2020-04-03 20:26:52 +02:00
|
|
|
"github.com/golang/glog"
|
2020-03-09 18:35:20 +01:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
|
2020-04-14 10:30:25 +02:00
|
|
|
"github.com/pulumi/pulumi/pkg/v2/codegen"
|
|
|
|
"github.com/pulumi/pulumi/pkg/v2/codegen/dotnet"
|
|
|
|
go_gen "github.com/pulumi/pulumi/pkg/v2/codegen/go"
|
|
|
|
"github.com/pulumi/pulumi/pkg/v2/codegen/nodejs"
|
|
|
|
"github.com/pulumi/pulumi/pkg/v2/codegen/python"
|
|
|
|
"github.com/pulumi/pulumi/pkg/v2/codegen/schema"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v2/go/common/util/contract"
|
2020-02-27 19:04:34 +01:00
|
|
|
)
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
var (
|
|
|
|
supportedLanguages = []string{"csharp", "go", "nodejs", "python"}
|
2020-06-18 21:32:15 +02:00
|
|
|
snippetLanguages = []string{"csharp", "go", "python", "typescript"}
|
2020-03-12 01:58:12 +01:00
|
|
|
templates *template.Template
|
|
|
|
packagedTemplates map[string][]byte
|
|
|
|
docHelpers map[string]codegen.DocLanguageHelper
|
2020-03-19 20:18:18 +01:00
|
|
|
|
|
|
|
// The following property case maps are for rendering property
|
|
|
|
// names of nested properties in Python language with the correct
|
|
|
|
// casing.
|
|
|
|
snakeCaseToCamelCase map[string]string
|
|
|
|
camelCaseToSnakeCase map[string]string
|
2020-06-26 20:47:13 +02:00
|
|
|
seenCasingTypes codegen.Set
|
2020-04-03 20:26:52 +02:00
|
|
|
|
|
|
|
// The language-specific info objects for a certain package (provider).
|
2020-04-21 01:36:05 +02:00
|
|
|
goPkgInfo go_gen.GoPackageInfo
|
2020-04-03 20:26:52 +02:00
|
|
|
csharpPkgInfo dotnet.CSharpPackageInfo
|
2020-04-11 03:28:14 +02:00
|
|
|
nodePkgInfo nodejs.NodePackageInfo
|
2020-08-19 17:34:06 +02:00
|
|
|
pythonPkgInfo python.PackageInfo
|
2020-04-03 20:26:52 +02:00
|
|
|
|
|
|
|
// langModuleNameLookup is a map of module name to its language-specific
|
|
|
|
// name.
|
|
|
|
langModuleNameLookup map[string]string
|
2020-04-09 18:43:38 +02:00
|
|
|
// titleLookup is a map to map module package name to the desired display name
|
|
|
|
// for display in the TOC menu under API Reference.
|
|
|
|
titleLookup = map[string]string{
|
2020-09-16 11:28:34 +02:00
|
|
|
"aiven": "Aiven",
|
|
|
|
"akamai": "Akamai",
|
|
|
|
"alicloud": "AliCloud",
|
|
|
|
"auth0": "Auth0",
|
|
|
|
"aws": "AWS",
|
|
|
|
"azure": "Azure",
|
|
|
|
"azure-nextgen": "Azure NextGen",
|
|
|
|
"azuread": "Azure AD",
|
|
|
|
"azuredevops": "Azure DevOps",
|
|
|
|
"azuresel": "Azure",
|
|
|
|
"civo": "Civo",
|
|
|
|
"cloudamqp": "CloudAMQP",
|
|
|
|
"cloudflare": "Cloudflare",
|
|
|
|
"consul": "Consul",
|
|
|
|
"datadog": "Datadog",
|
|
|
|
"digitalocean": "DigitalOcean",
|
|
|
|
"dnsimple": "DNSimple",
|
|
|
|
"docker": "Docker",
|
|
|
|
"f5bigip": "f5 BIG-IP",
|
|
|
|
"fastly": "Fastly",
|
|
|
|
"gcp": "GCP",
|
|
|
|
"github": "GitHub",
|
|
|
|
"gitlab": "GitLab",
|
|
|
|
"hcloud": "Hetzner Cloud",
|
|
|
|
"kafka": "Kafka",
|
|
|
|
"keycloak": "Keycloak",
|
|
|
|
"kong": "Kong",
|
|
|
|
"kubernetes": "Kubernetes",
|
|
|
|
"linode": "Linode",
|
|
|
|
"mailgun": "Mailgun",
|
|
|
|
"mongodbatlas": "MongoDB Atlas",
|
|
|
|
"mysql": "MySQL",
|
|
|
|
"newrelic": "New Relic",
|
|
|
|
"ns1": "NS1",
|
|
|
|
"okta": "Okta",
|
|
|
|
"openstack": "Open Stack",
|
|
|
|
"packet": "Packet",
|
|
|
|
"pagerduty": "PagerDuty",
|
|
|
|
"postgresql": "PostgreSQL",
|
|
|
|
"rabbitmq": "RabbitMQ",
|
|
|
|
"rancher2": "Rancher 2",
|
|
|
|
"random": "Random",
|
|
|
|
"signalfx": "SignalFx",
|
|
|
|
"spotinst": "Spotinst",
|
|
|
|
"tls": "TLS",
|
|
|
|
"vault": "Vault",
|
2020-10-23 21:25:37 +02:00
|
|
|
"venafi": "Venafi",
|
2020-09-16 11:28:34 +02:00
|
|
|
"vsphere": "vSphere",
|
|
|
|
"wavefront": "Wavefront",
|
2020-04-09 18:43:38 +02:00
|
|
|
}
|
2020-04-21 13:28:44 +02:00
|
|
|
// metaDescriptionRegexp attempts to extract the description from Resource.Comment.
|
|
|
|
// Extracts the first line, essentially the "human-friendly" part of the description.
|
|
|
|
metaDescriptionRegexp = regexp.MustCompile(`(?m)^.*$`)
|
2020-05-22 23:53:34 +02:00
|
|
|
// Property anchor tag separator, used in a property anchor tag id to separate the
|
|
|
|
// property and language (e.g. property~lang).
|
|
|
|
propertyLangSeparator = "_"
|
2020-03-12 01:58:12 +01:00
|
|
|
)
|
2020-03-09 18:35:20 +01:00
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
func init() {
|
|
|
|
docHelpers = make(map[string]codegen.DocLanguageHelper)
|
|
|
|
for _, lang := range supportedLanguages {
|
|
|
|
switch lang {
|
|
|
|
case "csharp":
|
2020-03-20 16:17:58 +01:00
|
|
|
docHelpers[lang] = &dotnet.DocLanguageHelper{}
|
2020-03-12 01:58:12 +01:00
|
|
|
case "go":
|
2020-03-20 16:17:58 +01:00
|
|
|
docHelpers[lang] = &go_gen.DocLanguageHelper{}
|
2020-03-12 01:58:12 +01:00
|
|
|
case "nodejs":
|
2020-03-20 16:17:58 +01:00
|
|
|
docHelpers[lang] = &nodejs.DocLanguageHelper{}
|
2020-03-12 01:58:12 +01:00
|
|
|
case "python":
|
2020-03-20 16:17:58 +01:00
|
|
|
docHelpers[lang] = &python.DocLanguageHelper{}
|
2020-03-12 01:58:12 +01:00
|
|
|
}
|
|
|
|
}
|
2020-03-19 20:18:18 +01:00
|
|
|
|
|
|
|
snakeCaseToCamelCase = map[string]string{}
|
|
|
|
camelCaseToSnakeCase = map[string]string{}
|
2020-06-26 20:47:13 +02:00
|
|
|
seenCasingTypes = codegen.Set{}
|
2020-04-03 20:26:52 +02:00
|
|
|
langModuleNameLookup = map[string]string{}
|
2020-03-12 01:58:12 +01:00
|
|
|
}
|
2020-03-09 18:35:20 +01:00
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
// header represents the header of each resource markdown file.
|
|
|
|
type header struct {
|
2020-04-21 13:28:44 +02:00
|
|
|
Title string
|
|
|
|
TitleTag string
|
|
|
|
MetaDesc string
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
// property represents an input or an output property.
|
|
|
|
type property struct {
|
2020-05-22 23:53:34 +02:00
|
|
|
// ID is the `id` attribute that will be attached to the DOM element containing the property.
|
|
|
|
ID string
|
2020-03-20 16:17:58 +01:00
|
|
|
// DisplayName is the property name with word-breaks.
|
|
|
|
DisplayName string
|
2020-03-09 18:35:20 +01:00
|
|
|
Name string
|
|
|
|
Comment string
|
2020-03-12 01:58:12 +01:00
|
|
|
Type propertyType
|
2020-03-09 18:35:20 +01:00
|
|
|
DeprecationMessage string
|
2020-05-22 23:53:34 +02:00
|
|
|
Link string
|
2020-03-09 18:35:20 +01:00
|
|
|
|
|
|
|
IsRequired bool
|
2020-03-12 01:58:12 +01:00
|
|
|
IsInput bool
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
// apiTypeDocLinks represents the links for a type's input and output API doc.
|
|
|
|
type apiTypeDocLinks struct {
|
|
|
|
InputType string
|
|
|
|
OutputType string
|
|
|
|
}
|
|
|
|
|
|
|
|
// docNestedType represents a complex type.
|
|
|
|
type docNestedType struct {
|
2020-03-09 18:35:20 +01:00
|
|
|
Name string
|
2020-04-18 11:45:47 +02:00
|
|
|
AnchorID string
|
2020-03-12 01:58:12 +01:00
|
|
|
APIDocLinks map[string]apiTypeDocLinks
|
|
|
|
Properties map[string][]property
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
// propertyType represents the type of a property.
|
|
|
|
type propertyType struct {
|
2020-03-23 22:17:14 +01:00
|
|
|
DisplayName string
|
|
|
|
Name string
|
2020-03-09 18:35:20 +01:00
|
|
|
// Link can be a link to an anchor tag on the same
|
|
|
|
// page, or to another page/site.
|
|
|
|
Link string
|
|
|
|
}
|
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
// formalParam represents the formal parameters of a constructor
|
|
|
|
// or a lookup function.
|
|
|
|
type formalParam struct {
|
2020-03-09 18:35:20 +01:00
|
|
|
Name string
|
2020-03-12 01:58:12 +01:00
|
|
|
Type propertyType
|
2020-03-09 18:35:20 +01:00
|
|
|
|
|
|
|
// This is the language specific optional type indicator.
|
|
|
|
// For example, in nodejs this is the character "?" and in Go
|
|
|
|
// it's "*".
|
|
|
|
OptionalFlag string
|
|
|
|
|
|
|
|
DefaultValue string
|
2020-04-17 20:46:02 +02:00
|
|
|
|
|
|
|
// Comment is an optional description of the parameter.
|
|
|
|
Comment string
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
|
|
|
|
2020-04-02 02:22:23 +02:00
|
|
|
type packageDetails struct {
|
|
|
|
Repository string
|
|
|
|
License string
|
|
|
|
Notes string
|
2020-04-17 20:39:19 +02:00
|
|
|
Version string
|
2020-04-02 02:22:23 +02:00
|
|
|
}
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
type resourceDocArgs struct {
|
|
|
|
Header header
|
2020-03-09 18:35:20 +01:00
|
|
|
|
2020-04-17 20:45:06 +02:00
|
|
|
Tool string
|
|
|
|
// LangChooserLanguages is a comma-separated list of languages to pass to the
|
2020-05-16 02:33:56 +02:00
|
|
|
// language chooser shortcode. Use this to customize the languages shown for a
|
|
|
|
// resource. By default, the language chooser will show all languages supported
|
|
|
|
// by Pulumi for all resources.
|
2020-04-17 20:45:06 +02:00
|
|
|
LangChooserLanguages string
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
// Comment represents the introductory resource comment.
|
2020-04-07 02:01:33 +02:00
|
|
|
Comment string
|
2020-05-09 01:25:28 +02:00
|
|
|
ExamplesSection []exampleSection
|
2020-04-07 02:01:33 +02:00
|
|
|
DeprecationMessage string
|
2020-03-09 18:35:20 +01:00
|
|
|
|
2020-11-09 15:12:58 +01:00
|
|
|
// Import
|
|
|
|
ImportDocs string
|
|
|
|
|
2020-04-17 20:46:02 +02:00
|
|
|
// ConstructorParams is a map from language to the rendered HTML for the constructor's
|
|
|
|
// arguments.
|
2020-03-09 18:35:20 +01:00
|
|
|
ConstructorParams map[string]string
|
2020-04-17 20:46:02 +02:00
|
|
|
// ConstructorParamsTyped is the typed set of parameters for the constructor, in order.
|
|
|
|
ConstructorParamsTyped map[string][]formalParam
|
2020-03-09 18:35:20 +01:00
|
|
|
// ConstructorResource is the resource that is being constructed or
|
|
|
|
// is the result of a constructor-like function.
|
2020-03-12 01:58:12 +01:00
|
|
|
ConstructorResource map[string]propertyType
|
|
|
|
// ArgsRequired is a flag indicating if the args param is required
|
|
|
|
// when creating a new resource.
|
|
|
|
ArgsRequired bool
|
|
|
|
|
|
|
|
// InputProperties is a map per language and a corresponding slice of
|
|
|
|
// input properties accepted as args while creating a new resource.
|
|
|
|
InputProperties map[string][]property
|
|
|
|
// OutputProperties is a map per language and a corresponding slice of
|
|
|
|
// output properties returned when a new instance of the resource is
|
|
|
|
// created.
|
|
|
|
OutputProperties map[string][]property
|
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
// LookupParams is a map of the param string to be rendered per language
|
|
|
|
// for looking-up a resource.
|
|
|
|
LookupParams map[string]string
|
2020-03-12 01:58:12 +01:00
|
|
|
// StateInputs is a map per language and the corresponding slice of
|
|
|
|
// state input properties required while looking-up an existing resource.
|
|
|
|
StateInputs map[string][]property
|
|
|
|
// StateParam is the type name of the state param, if any.
|
|
|
|
StateParam string
|
|
|
|
|
|
|
|
// NestedTypes is a slice of the nested types used in the input and
|
|
|
|
// output properties.
|
|
|
|
NestedTypes []docNestedType
|
2020-04-02 02:22:23 +02:00
|
|
|
|
|
|
|
PackageDetails packageDetails
|
2020-03-12 01:58:12 +01:00
|
|
|
}
|
2020-03-09 18:35:20 +01:00
|
|
|
|
2020-04-03 20:26:52 +02:00
|
|
|
// typeUsage represents a nested type's usage.
|
|
|
|
type typeUsage struct {
|
2020-03-12 01:58:12 +01:00
|
|
|
Input bool
|
|
|
|
Output bool
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
// nestedTypeUsageInfo is a type-alias for a map of Pulumi type-tokens
|
2020-04-03 20:26:52 +02:00
|
|
|
// and whether or not the type is used as an input and/or output
|
2020-03-12 01:58:12 +01:00
|
|
|
// properties.
|
2020-04-03 20:26:52 +02:00
|
|
|
type nestedTypeUsageInfo map[string]typeUsage
|
2020-03-12 01:58:12 +01:00
|
|
|
|
|
|
|
func (ss nestedTypeUsageInfo) add(s string, input bool) {
|
|
|
|
if v, ok := ss[s]; ok {
|
|
|
|
if input {
|
|
|
|
v.Input = true
|
|
|
|
} else {
|
|
|
|
v.Output = true
|
|
|
|
}
|
|
|
|
ss[s] = v
|
|
|
|
return
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-04-03 20:26:52 +02:00
|
|
|
ss[s] = typeUsage{
|
2020-03-12 01:58:12 +01:00
|
|
|
Input: input,
|
|
|
|
Output: !input,
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 20:26:52 +02:00
|
|
|
// contains returns true if the token already exists and matches the
|
|
|
|
// input or output flag of the token.
|
|
|
|
func (ss nestedTypeUsageInfo) contains(token string, input bool) bool {
|
|
|
|
a, ok := ss[token]
|
|
|
|
if !ok {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if input && a.Input {
|
|
|
|
return true
|
|
|
|
} else if !input && a.Output {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type modContext struct {
|
2020-09-20 20:56:52 +02:00
|
|
|
pkg *schema.Package
|
|
|
|
mod string
|
|
|
|
resources []*schema.Resource
|
|
|
|
functions []*schema.Function
|
|
|
|
children []*modContext
|
|
|
|
tool string
|
|
|
|
emitAPILinks bool
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
func resourceName(r *schema.Resource) string {
|
|
|
|
if r.IsProvider {
|
|
|
|
return "Provider"
|
|
|
|
}
|
2020-04-28 02:47:01 +02:00
|
|
|
return strings.Title(tokenToName(r.Token))
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
func getLanguageDocHelper(lang string) codegen.DocLanguageHelper {
|
|
|
|
if h, ok := docHelpers[lang]; ok {
|
|
|
|
return h
|
|
|
|
}
|
|
|
|
panic(errors.Errorf("could not find a doc lang helper for %s", lang))
|
|
|
|
}
|
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
type propertyCharacteristics struct {
|
|
|
|
// input is a flag indicating if the property is an input type.
|
|
|
|
input bool
|
|
|
|
// optional is a flag indicating if the property is optional.
|
|
|
|
optional bool
|
|
|
|
}
|
|
|
|
|
2020-05-16 02:33:56 +02:00
|
|
|
// getLanguageModuleName transforms the current module's name to a
|
|
|
|
// language-specific name using the language info, if any, for the
|
|
|
|
// current package.
|
|
|
|
func (mod *modContext) getLanguageModuleName(lang string) string {
|
|
|
|
modName := mod.mod
|
2020-04-03 20:26:52 +02:00
|
|
|
lookupKey := lang + "_" + modName
|
|
|
|
if v, ok := langModuleNameLookup[lookupKey]; ok {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
switch lang {
|
|
|
|
case "go":
|
2020-08-22 00:34:57 +02:00
|
|
|
// Go module names use lowercase.
|
|
|
|
modName = strings.ToLower(modName)
|
2020-04-03 20:26:52 +02:00
|
|
|
if override, ok := goPkgInfo.ModuleToPackage[modName]; ok {
|
|
|
|
modName = override
|
|
|
|
}
|
|
|
|
case "csharp":
|
|
|
|
if override, ok := csharpPkgInfo.Namespaces[modName]; ok {
|
|
|
|
modName = override
|
|
|
|
}
|
2020-04-11 03:28:14 +02:00
|
|
|
case "nodejs":
|
|
|
|
if override, ok := nodePkgInfo.ModuleToPackage[modName]; ok {
|
|
|
|
modName = override
|
|
|
|
}
|
2020-08-19 17:34:06 +02:00
|
|
|
case "python":
|
|
|
|
if override, ok := pythonPkgInfo.ModuleNameOverrides[modName]; ok {
|
|
|
|
modName = override
|
|
|
|
}
|
2020-04-03 20:26:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
langModuleNameLookup[lookupKey] = modName
|
|
|
|
return modName
|
|
|
|
}
|
|
|
|
|
2020-04-07 02:01:33 +02:00
|
|
|
// cleanTypeString removes any namespaces from the generated type string for all languages.
|
|
|
|
// The result of this function should be used display purposes only.
|
|
|
|
func (mod *modContext) cleanTypeString(t schema.Type, langTypeString, lang, modName string, isInput bool) string {
|
2020-04-10 23:07:52 +02:00
|
|
|
switch lang {
|
2020-08-19 17:34:06 +02:00
|
|
|
case "go", "python":
|
2020-04-07 02:01:33 +02:00
|
|
|
parts := strings.Split(langTypeString, ".")
|
|
|
|
return parts[len(parts)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanCSharpName := func(pkgName, objModName string) string {
|
2020-04-10 23:07:52 +02:00
|
|
|
// C# types can be wrapped in enumerable types such as List<> or Dictionary<>, so we have to
|
|
|
|
// only replace the namespace between the < and the > characters.
|
|
|
|
qualifier := "Inputs"
|
|
|
|
if !isInput {
|
|
|
|
qualifier = "Outputs"
|
|
|
|
}
|
|
|
|
|
2020-04-07 02:01:33 +02:00
|
|
|
var csharpNS string
|
|
|
|
// This type could be at the package-level, so it won't have a module name.
|
|
|
|
if objModName != "" {
|
|
|
|
csharpNS = fmt.Sprintf("Pulumi.%s.%s.%s.", title(pkgName, lang), title(objModName, lang), qualifier)
|
|
|
|
} else {
|
|
|
|
csharpNS = fmt.Sprintf("Pulumi.%s.%s.", title(pkgName, lang), qualifier)
|
|
|
|
}
|
|
|
|
return strings.ReplaceAll(langTypeString, csharpNS, "")
|
|
|
|
}
|
|
|
|
|
2020-04-10 23:07:52 +02:00
|
|
|
cleanNodeJSName := func(objModName string) string {
|
2020-04-11 03:28:14 +02:00
|
|
|
// The nodejs codegen currently doesn't use the ModuleToPackage override available
|
|
|
|
// in the k8s package's schema. So we'll manually strip some known module names for k8s.
|
|
|
|
// TODO[pulumi/pulumi#4325]: Remove this block once the nodejs code gen is able to use the
|
|
|
|
// package name overrides for modules.
|
|
|
|
if isKubernetesPackage(mod.pkg) {
|
|
|
|
langTypeString = strings.ReplaceAll(langTypeString, "k8s.io.", "")
|
|
|
|
langTypeString = strings.ReplaceAll(langTypeString, "apiserver.", "")
|
|
|
|
langTypeString = strings.ReplaceAll(langTypeString, "rbac.authorization.v1.", "")
|
|
|
|
langTypeString = strings.ReplaceAll(langTypeString, "rbac.authorization.v1alpha1.", "")
|
|
|
|
langTypeString = strings.ReplaceAll(langTypeString, "rbac.authorization.v1beta1.", "")
|
|
|
|
}
|
2020-04-10 23:07:52 +02:00
|
|
|
objModName = strings.ReplaceAll(objModName, "/", ".") + "."
|
|
|
|
return strings.ReplaceAll(langTypeString, objModName, "")
|
|
|
|
}
|
|
|
|
|
|
|
|
switch t := t.(type) {
|
|
|
|
case *schema.ArrayType:
|
|
|
|
if schema.IsPrimitiveType(t.ElementType) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
return mod.cleanTypeString(t.ElementType, langTypeString, lang, modName, isInput)
|
|
|
|
case *schema.UnionType:
|
|
|
|
for _, e := range t.ElementTypes {
|
|
|
|
if schema.IsPrimitiveType(e) {
|
|
|
|
continue
|
2020-04-07 02:01:33 +02:00
|
|
|
}
|
2020-04-10 23:07:52 +02:00
|
|
|
return mod.cleanTypeString(e, langTypeString, lang, modName, isInput)
|
|
|
|
}
|
|
|
|
case *schema.ObjectType:
|
|
|
|
objTypeModName := mod.pkg.TokenToModule(t.Token)
|
|
|
|
if objTypeModName != mod.mod {
|
2020-05-16 02:33:56 +02:00
|
|
|
modName = mod.getLanguageModuleName(lang)
|
2020-04-07 02:01:33 +02:00
|
|
|
}
|
|
|
|
}
|
2020-04-10 23:07:52 +02:00
|
|
|
|
|
|
|
if lang == "nodejs" {
|
|
|
|
return cleanNodeJSName(modName)
|
|
|
|
} else if lang == "csharp" {
|
|
|
|
return cleanCSharpName(mod.pkg.Name, modName)
|
|
|
|
}
|
|
|
|
return strings.ReplaceAll(langTypeString, modName, "")
|
2020-04-07 02:01:33 +02:00
|
|
|
}
|
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
// typeString returns a property type suitable for docs with its display name and the anchor link to
|
|
|
|
// a type if the type of the property is an array or an object.
|
|
|
|
func (mod *modContext) typeString(t schema.Type, lang string, characteristics propertyCharacteristics, insertWordBreaks bool) propertyType {
|
2020-03-12 01:58:12 +01:00
|
|
|
docLanguageHelper := getLanguageDocHelper(lang)
|
2020-05-16 02:33:56 +02:00
|
|
|
modName := mod.getLanguageModuleName(lang)
|
2020-04-03 20:26:52 +02:00
|
|
|
langTypeString := docLanguageHelper.GetLanguageTypeString(mod.pkg, modName, t, characteristics.input, characteristics.optional)
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
// If the type is an object type, let's also wrap it with a link to the supporting type
|
|
|
|
// on the same page using an anchor tag.
|
|
|
|
var href string
|
2020-02-27 19:04:34 +01:00
|
|
|
switch t := t.(type) {
|
|
|
|
case *schema.ArrayType:
|
2020-03-17 21:37:40 +01:00
|
|
|
elementLangType := mod.typeString(t.ElementType, lang, characteristics, false)
|
2020-03-09 18:35:20 +01:00
|
|
|
href = elementLangType.Link
|
2020-02-27 19:04:34 +01:00
|
|
|
case *schema.ObjectType:
|
2020-03-09 18:35:20 +01:00
|
|
|
tokenName := tokenToName(t.Token)
|
2020-04-07 02:01:33 +02:00
|
|
|
// Links to anchor tags on the same page must be lower-cased.
|
|
|
|
href = "#" + strings.ToLower(tokenName)
|
2020-04-15 21:56:28 +02:00
|
|
|
default:
|
|
|
|
// Check if type is primitive/built-in type if no match for cases listed above.
|
|
|
|
if schema.IsPrimitiveType(t) {
|
|
|
|
href = docLanguageHelper.GetDocLinkForBuiltInType(t.String())
|
|
|
|
}
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
|
|
|
|
2020-03-23 22:17:14 +01:00
|
|
|
// Strip the namespace/module prefix for the type's display name.
|
2020-04-07 02:01:33 +02:00
|
|
|
displayName := langTypeString
|
|
|
|
if !schema.IsPrimitiveType(t) {
|
|
|
|
displayName = mod.cleanTypeString(t, langTypeString, lang, modName, characteristics.input)
|
2020-03-23 22:17:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// If word-breaks need to be inserted, then the type string
|
|
|
|
// should be html-encoded first if the language is C# in order
|
2020-03-30 23:37:30 +02:00
|
|
|
// to avoid confusing the Hugo rendering where the word-break
|
2020-03-23 22:17:14 +01:00
|
|
|
// tags are inserted.
|
2020-03-09 18:35:20 +01:00
|
|
|
if insertWordBreaks {
|
|
|
|
if lang == "csharp" {
|
2020-03-23 22:17:14 +01:00
|
|
|
displayName = html.EscapeString(displayName)
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
2020-03-23 22:17:14 +01:00
|
|
|
displayName = wbr(displayName)
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
2020-04-14 22:34:53 +02:00
|
|
|
|
|
|
|
displayName = cleanOptionalIdentifier(displayName, lang)
|
|
|
|
langTypeString = cleanOptionalIdentifier(langTypeString, lang)
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
return propertyType{
|
2020-03-23 22:17:14 +01:00
|
|
|
Name: langTypeString,
|
|
|
|
DisplayName: displayName,
|
|
|
|
Link: href,
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-14 22:34:53 +02:00
|
|
|
// cleanOptionalIdentifier removes the type identifier (i.e. "?" in "string?").
|
|
|
|
func cleanOptionalIdentifier(s, lang string) string {
|
|
|
|
switch lang {
|
|
|
|
case "nodejs":
|
|
|
|
return strings.TrimSuffix(s, "?")
|
|
|
|
case "go":
|
|
|
|
return strings.TrimPrefix(s, "*")
|
|
|
|
case "csharp":
|
|
|
|
return strings.TrimSuffix(s, "?")
|
|
|
|
case "python":
|
2020-08-19 17:34:06 +02:00
|
|
|
if strings.HasPrefix(s, "Optional[") && strings.HasSuffix(s, "]") {
|
|
|
|
s = strings.TrimPrefix(s, "Optional[")
|
|
|
|
s = strings.TrimSuffix(s, "]")
|
|
|
|
return s
|
|
|
|
}
|
2020-04-14 22:34:53 +02:00
|
|
|
}
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2020-04-17 20:46:02 +02:00
|
|
|
// Resources typically take the same set of parameters to their constructors, and these
|
|
|
|
// are the default comments/descriptions for them.
|
|
|
|
const (
|
|
|
|
ctorNameArgComment = "The unique name of the resource."
|
|
|
|
ctorArgsArgComment = "The arguments to resource properties."
|
|
|
|
ctorOptsArgComment = "Bag of options to control resource's behavior."
|
|
|
|
)
|
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
func (mod *modContext) genConstructorTS(r *schema.Resource, argsOptional bool) []formalParam {
|
2020-02-27 19:04:34 +01:00
|
|
|
name := resourceName(r)
|
2020-04-14 02:27:38 +02:00
|
|
|
docLangHelper := getLanguageDocHelper("nodejs")
|
|
|
|
// Use the NodeJS module to package lookup to transform the module name to its normalized package name.
|
2020-05-16 02:33:56 +02:00
|
|
|
modName := mod.getLanguageModuleName("nodejs")
|
2020-04-14 02:27:38 +02:00
|
|
|
|
|
|
|
var argsType string
|
|
|
|
var argsDocLink string
|
2020-06-22 18:27:17 +02:00
|
|
|
optsType := "CustomResourceOptions"
|
2020-05-06 21:11:31 +02:00
|
|
|
// The args type for k8s package differs from the rest depending on whether we are dealing with
|
|
|
|
// overlay resources or regular k8s resources.
|
2020-04-14 02:27:38 +02:00
|
|
|
if isKubernetesPackage(mod.pkg) {
|
2020-05-06 21:11:31 +02:00
|
|
|
if mod.isKubernetesOverlayModule() {
|
|
|
|
if name == "CustomResource" {
|
|
|
|
argsType = name + "Args"
|
|
|
|
} else {
|
|
|
|
argsType = name + "Opts"
|
|
|
|
}
|
|
|
|
argsDocLink = docLangHelper.GetDocLinkForResourceType(mod.pkg, modName, argsType)
|
|
|
|
} else {
|
|
|
|
// The non-schema-based k8s codegen does not apply a suffix to the input types.
|
|
|
|
argsType = name
|
|
|
|
// The args types themselves are all under the input types module path, so use the input type link for the args type.
|
|
|
|
argsDocLink = docLangHelper.GetDocLinkForResourceInputOrOutputType(mod.pkg, modName, argsType, true)
|
|
|
|
}
|
2020-06-22 18:27:17 +02:00
|
|
|
|
|
|
|
if mod.isComponentResource() {
|
|
|
|
optsType = "ComponentResourceOptions"
|
|
|
|
}
|
2020-04-14 02:27:38 +02:00
|
|
|
} else {
|
|
|
|
argsType = name + "Args"
|
2020-05-06 21:11:31 +02:00
|
|
|
// All args types are in the same module path as the resource class itself even though it is an "input" type.
|
2020-09-20 20:56:52 +02:00
|
|
|
if mod.emitAPILinks {
|
|
|
|
argsDocLink = docLangHelper.GetDocLinkForResourceType(mod.pkg, modName, argsType)
|
|
|
|
}
|
2020-04-14 02:27:38 +02:00
|
|
|
}
|
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
argsFlag := ""
|
|
|
|
if argsOptional {
|
|
|
|
argsFlag = "?"
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
return []formalParam{
|
2020-03-09 18:35:20 +01:00
|
|
|
{
|
|
|
|
Name: "name",
|
2020-03-12 01:58:12 +01:00
|
|
|
Type: propertyType{
|
2020-03-09 18:35:20 +01:00
|
|
|
Name: "string",
|
2020-04-15 21:56:28 +02:00
|
|
|
Link: docLangHelper.GetDocLinkForBuiltInType("string"),
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
2020-04-17 20:46:02 +02:00
|
|
|
Comment: ctorNameArgComment,
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "args",
|
|
|
|
OptionalFlag: argsFlag,
|
2020-03-12 01:58:12 +01:00
|
|
|
Type: propertyType{
|
2020-03-09 18:35:20 +01:00
|
|
|
Name: argsType,
|
2020-04-14 02:27:38 +02:00
|
|
|
Link: argsDocLink,
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
2020-04-17 20:46:02 +02:00
|
|
|
Comment: ctorArgsArgComment,
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "opts",
|
|
|
|
OptionalFlag: "?",
|
2020-03-12 01:58:12 +01:00
|
|
|
Type: propertyType{
|
2020-06-22 18:27:17 +02:00
|
|
|
Name: optsType,
|
|
|
|
Link: docLangHelper.GetDocLinkForPulumiType(mod.pkg, optsType),
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
2020-04-17 20:46:02 +02:00
|
|
|
Comment: ctorOptsArgComment,
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
func (mod *modContext) genConstructorGo(r *schema.Resource, argsOptional bool) []formalParam {
|
2020-02-27 19:04:34 +01:00
|
|
|
name := resourceName(r)
|
|
|
|
argsType := name + "Args"
|
2020-03-09 18:35:20 +01:00
|
|
|
argsFlag := ""
|
|
|
|
if argsOptional {
|
|
|
|
argsFlag = "*"
|
|
|
|
}
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
docLangHelper := getLanguageDocHelper("go")
|
2020-04-11 03:28:14 +02:00
|
|
|
// Use the Go module to package lookup to transform the module name to its normalized package name.
|
2020-05-16 02:33:56 +02:00
|
|
|
modName := mod.getLanguageModuleName("go")
|
2020-09-20 20:56:52 +02:00
|
|
|
|
|
|
|
var argsTypeLink string
|
|
|
|
if mod.emitAPILinks {
|
|
|
|
argsTypeLink = docLangHelper.GetDocLinkForResourceType(mod.pkg, modName, argsType)
|
|
|
|
}
|
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
return []formalParam{
|
2020-03-09 18:35:20 +01:00
|
|
|
{
|
|
|
|
Name: "ctx",
|
|
|
|
OptionalFlag: "*",
|
2020-03-12 01:58:12 +01:00
|
|
|
Type: propertyType{
|
2020-04-14 02:27:38 +02:00
|
|
|
Name: "Context",
|
2020-04-15 20:58:08 +02:00
|
|
|
Link: docLangHelper.GetDocLinkForPulumiType(mod.pkg, "Context"),
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
2020-04-18 22:06:43 +02:00
|
|
|
Comment: "Context object for the current deployment.",
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "name",
|
2020-03-12 01:58:12 +01:00
|
|
|
Type: propertyType{
|
2020-03-09 18:35:20 +01:00
|
|
|
Name: "string",
|
2020-04-15 21:56:28 +02:00
|
|
|
Link: docLangHelper.GetDocLinkForBuiltInType("string"),
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
2020-04-17 20:46:02 +02:00
|
|
|
Comment: ctorNameArgComment,
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "args",
|
|
|
|
OptionalFlag: argsFlag,
|
2020-03-12 01:58:12 +01:00
|
|
|
Type: propertyType{
|
2020-03-09 18:35:20 +01:00
|
|
|
Name: argsType,
|
2020-09-20 20:56:52 +02:00
|
|
|
Link: argsTypeLink,
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
2020-04-17 20:46:02 +02:00
|
|
|
Comment: ctorArgsArgComment,
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "opts",
|
|
|
|
OptionalFlag: "...",
|
2020-03-12 01:58:12 +01:00
|
|
|
Type: propertyType{
|
2020-04-14 02:27:38 +02:00
|
|
|
Name: "ResourceOption",
|
2020-04-15 20:58:08 +02:00
|
|
|
Link: docLangHelper.GetDocLinkForPulumiType(mod.pkg, "ResourceOption"),
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
2020-04-17 20:46:02 +02:00
|
|
|
Comment: ctorOptsArgComment,
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
func (mod *modContext) genConstructorCS(r *schema.Resource, argsOptional bool) []formalParam {
|
2020-02-27 19:04:34 +01:00
|
|
|
name := resourceName(r)
|
2020-03-09 18:35:20 +01:00
|
|
|
argsSchemaType := &schema.ObjectType{
|
2020-11-24 11:20:25 +01:00
|
|
|
Token: r.Token,
|
|
|
|
Package: mod.pkg,
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
2020-04-03 20:26:52 +02:00
|
|
|
// Get the C#-specific name for the args type, which will be the fully-qualified name.
|
2020-03-17 21:37:40 +01:00
|
|
|
characteristics := propertyCharacteristics{
|
|
|
|
input: true,
|
|
|
|
optional: argsOptional,
|
|
|
|
}
|
2020-04-14 02:27:38 +02:00
|
|
|
|
|
|
|
var argLangTypeName string
|
2020-06-22 18:27:17 +02:00
|
|
|
optsType := "CustomResourceOptions"
|
|
|
|
|
2020-05-06 21:11:31 +02:00
|
|
|
// Constructor argument types in the k8s package for C# use a different namespace path.
|
|
|
|
// K8s overlay resources are in the same namespace path as the resource itself.
|
2020-04-14 02:27:38 +02:00
|
|
|
if isKubernetesPackage(mod.pkg) {
|
2020-05-06 21:11:31 +02:00
|
|
|
if mod.mod != "" {
|
2020-05-16 02:33:56 +02:00
|
|
|
correctModName := mod.getLanguageModuleName("csharp")
|
2020-05-06 21:11:31 +02:00
|
|
|
if !mod.isKubernetesOverlayModule() {
|
|
|
|
// For k8s, the args type for a resource is part of the `Types.Inputs` namespace.
|
|
|
|
argLangTypeName = "Pulumi.Kubernetes.Types.Inputs." + correctModName + "." + name + "Args"
|
|
|
|
} else {
|
|
|
|
// Helm's resource args type does not use the version number.
|
|
|
|
if strings.HasPrefix(mod.mod, "helm") {
|
|
|
|
correctModName = "Helm"
|
|
|
|
}
|
|
|
|
argLangTypeName = "Pulumi.Kubernetes." + correctModName + "." + name + "Args"
|
|
|
|
}
|
2020-04-14 02:27:38 +02:00
|
|
|
} else {
|
|
|
|
argLangTypeName = "Pulumi.Kubernetes." + name + "Args"
|
|
|
|
}
|
2020-06-22 18:27:17 +02:00
|
|
|
|
|
|
|
if mod.isComponentResource() {
|
|
|
|
optsType = "ComponentResourceOptions"
|
|
|
|
}
|
2020-04-14 02:27:38 +02:00
|
|
|
} else {
|
|
|
|
argLangType := mod.typeString(argsSchemaType, "csharp", characteristics, false)
|
|
|
|
argLangTypeName = strings.ReplaceAll(argLangType.Name, "Inputs.", "")
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
var argsFlag string
|
2020-02-27 19:04:34 +01:00
|
|
|
var argsDefault string
|
2020-03-09 18:35:20 +01:00
|
|
|
if argsOptional {
|
2020-02-27 19:04:34 +01:00
|
|
|
// If the number of required input properties was zero, we can make the args object optional.
|
|
|
|
argsDefault = " = null"
|
2020-03-09 18:35:20 +01:00
|
|
|
argsFlag = "?"
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
docLangHelper := getLanguageDocHelper("csharp")
|
2020-09-20 20:56:52 +02:00
|
|
|
|
|
|
|
var argsTypeLink string
|
|
|
|
if mod.emitAPILinks {
|
|
|
|
argsTypeLink = docLangHelper.GetDocLinkForResourceType(mod.pkg, "", argLangTypeName)
|
|
|
|
}
|
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
return []formalParam{
|
2020-03-09 18:35:20 +01:00
|
|
|
{
|
|
|
|
Name: "name",
|
2020-03-12 01:58:12 +01:00
|
|
|
Type: propertyType{
|
2020-03-09 18:35:20 +01:00
|
|
|
Name: "string",
|
2020-04-15 21:56:28 +02:00
|
|
|
Link: docLangHelper.GetDocLinkForBuiltInType("string"),
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
2020-04-17 20:46:02 +02:00
|
|
|
Comment: ctorNameArgComment,
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "args",
|
|
|
|
OptionalFlag: argsFlag,
|
|
|
|
DefaultValue: argsDefault,
|
2020-03-12 01:58:12 +01:00
|
|
|
Type: propertyType{
|
2020-03-17 21:37:40 +01:00
|
|
|
Name: name + "Args",
|
2020-09-20 20:56:52 +02:00
|
|
|
Link: argsTypeLink,
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
2020-04-17 20:46:02 +02:00
|
|
|
Comment: ctorArgsArgComment,
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "opts",
|
|
|
|
OptionalFlag: "?",
|
|
|
|
DefaultValue: " = null",
|
2020-03-12 01:58:12 +01:00
|
|
|
Type: propertyType{
|
2020-06-22 18:27:17 +02:00
|
|
|
Name: optsType,
|
|
|
|
Link: docLangHelper.GetDocLinkForPulumiType(mod.pkg, fmt.Sprintf("Pulumi.%s", optsType)),
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
2020-04-17 20:46:02 +02:00
|
|
|
Comment: ctorOptsArgComment,
|
2020-03-09 18:35:20 +01:00
|
|
|
},
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-19 17:34:06 +02:00
|
|
|
func (mod *modContext) genConstructorPython(r *schema.Resource, argsOptional bool) []formalParam {
|
|
|
|
docLanguageHelper := getLanguageDocHelper("python")
|
|
|
|
isK8sOverlayMod := mod.isKubernetesOverlayModule()
|
|
|
|
isDockerImageResource := mod.pkg.Name == "docker" && resourceName(r) == "Image"
|
|
|
|
|
|
|
|
// Kubernetes overlay resources use a different ordering of formal params in Python.
|
|
|
|
if isK8sOverlayMod {
|
|
|
|
return getKubernetesOverlayPythonFormalParams(mod.mod)
|
|
|
|
} else if isDockerImageResource {
|
|
|
|
return getDockerImagePythonFormalParams()
|
|
|
|
}
|
|
|
|
|
|
|
|
params := make([]formalParam, 0, len(r.InputProperties)+1)
|
|
|
|
// All other resources accept the resource options as a second parameter.
|
|
|
|
params = append(params, formalParam{
|
|
|
|
Name: "opts",
|
|
|
|
DefaultValue: " = None",
|
|
|
|
Type: propertyType{
|
|
|
|
Name: "Optional[ResourceOptions]",
|
|
|
|
Link: "/docs/reference/pkg/python/pulumi/#pulumi.ResourceOptions",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
for _, p := range r.InputProperties {
|
|
|
|
// If the property defines a const value, then skip it.
|
|
|
|
// For example, in k8s, `apiVersion` and `kind` are often hard-coded
|
|
|
|
// in the SDK and are not really user-provided input properties.
|
|
|
|
if p.ConstValue != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
typ := docLanguageHelper.GetLanguageTypeString(mod.pkg, mod.mod, p.Type, true /*input*/, false /*optional*/)
|
|
|
|
params = append(params, formalParam{
|
|
|
|
Name: python.InitParamName(p.Name),
|
|
|
|
DefaultValue: " = None",
|
|
|
|
Type: propertyType{
|
|
|
|
Name: fmt.Sprintf("Optional[%s]", typ),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return params
|
|
|
|
}
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
func (mod *modContext) genNestedTypes(member interface{}, resourceType bool) []docNestedType {
|
|
|
|
tokens := nestedTypeUsageInfo{}
|
|
|
|
// Collect all of the types for this "member" as a map of resource names
|
|
|
|
// and if it appears in an input object and/or output object.
|
|
|
|
mod.getTypes(member, tokens)
|
2020-03-09 18:35:20 +01:00
|
|
|
|
2020-05-02 02:13:56 +02:00
|
|
|
isK8s := isKubernetesPackage(mod.pkg)
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
var objs []docNestedType
|
2020-04-03 20:26:52 +02:00
|
|
|
for token, tyUsage := range tokens {
|
2020-02-27 19:04:34 +01:00
|
|
|
for _, t := range mod.pkg.Types {
|
2020-04-07 02:01:33 +02:00
|
|
|
obj, ok := t.(*schema.ObjectType)
|
|
|
|
if !ok || obj.Token != token {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if len(obj.Properties) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
2020-03-09 18:35:20 +01:00
|
|
|
|
2020-04-07 02:01:33 +02:00
|
|
|
// Create maps to hold the per-language properties of this object and links to
|
|
|
|
// the API doc for each language.
|
|
|
|
props := make(map[string][]property)
|
|
|
|
apiDocLinks := make(map[string]apiTypeDocLinks)
|
|
|
|
for _, lang := range supportedLanguages {
|
|
|
|
// The nested type may be under a different package in a language.
|
|
|
|
// For example, in k8s, common types are in the core/v1 module and can appear in
|
|
|
|
// nested types elsewhere. So we use the appropriate name of that type,
|
|
|
|
// as well as its language-specific name. For example, module name for use as a C# namespace
|
|
|
|
// or as a Go package name.
|
2020-05-16 02:33:56 +02:00
|
|
|
modName := mod.getLanguageModuleName(lang)
|
2020-04-07 02:01:33 +02:00
|
|
|
nestedTypeModName := mod.pkg.TokenToModule(token)
|
|
|
|
if nestedTypeModName != mod.mod {
|
2020-05-16 02:33:56 +02:00
|
|
|
modName = mod.getLanguageModuleName(lang)
|
2020-04-07 02:01:33 +02:00
|
|
|
}
|
2020-04-03 20:26:52 +02:00
|
|
|
|
2020-04-07 02:01:33 +02:00
|
|
|
docLangHelper := getLanguageDocHelper(lang)
|
|
|
|
inputCharacteristics := propertyCharacteristics{
|
|
|
|
input: true,
|
|
|
|
optional: true,
|
|
|
|
}
|
|
|
|
outputCharacteristics := propertyCharacteristics{
|
|
|
|
input: false,
|
|
|
|
optional: true,
|
|
|
|
}
|
|
|
|
inputObjLangType := mod.typeString(t, lang, inputCharacteristics, false /*insertWordBreaks*/)
|
|
|
|
outputObjLangType := mod.typeString(t, lang, outputCharacteristics, false /*insertWordBreaks*/)
|
|
|
|
|
|
|
|
// Get the doc link for this nested type based on whether the type is for a Function or a Resource.
|
|
|
|
var inputTypeDocLink string
|
|
|
|
var outputTypeDocLink string
|
|
|
|
if resourceType {
|
|
|
|
if tyUsage.Input {
|
2020-04-14 22:59:23 +02:00
|
|
|
inputTypeDocLink = docLangHelper.GetDocLinkForResourceInputOrOutputType(mod.pkg, modName, inputObjLangType.Name, true)
|
2020-03-17 21:37:40 +01:00
|
|
|
}
|
2020-04-07 02:01:33 +02:00
|
|
|
if tyUsage.Output {
|
2020-04-14 22:59:23 +02:00
|
|
|
outputTypeDocLink = docLangHelper.GetDocLinkForResourceInputOrOutputType(mod.pkg, modName, outputObjLangType.Name, false)
|
2020-03-17 21:37:40 +01:00
|
|
|
}
|
2020-04-07 02:01:33 +02:00
|
|
|
} else {
|
|
|
|
if tyUsage.Input {
|
2020-04-14 22:59:23 +02:00
|
|
|
inputTypeDocLink = docLangHelper.GetDocLinkForFunctionInputOrOutputType(mod.pkg, modName, inputObjLangType.Name, true)
|
2020-03-12 01:58:12 +01:00
|
|
|
}
|
2020-04-07 02:01:33 +02:00
|
|
|
if tyUsage.Output {
|
2020-04-14 22:59:23 +02:00
|
|
|
outputTypeDocLink = docLangHelper.GetDocLinkForFunctionInputOrOutputType(mod.pkg, modName, outputObjLangType.Name, false)
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
|
|
|
}
|
2020-05-02 02:13:56 +02:00
|
|
|
|
|
|
|
props[lang] = mod.getProperties(obj.Properties, lang, true, true)
|
|
|
|
// Don't add C# type links for Kubernetes because there are differences in the namespaces between the schema code gen and
|
|
|
|
// the current code gen that the package uses. So the links will be incorrect.
|
|
|
|
if isK8s && lang == "csharp" {
|
|
|
|
continue
|
|
|
|
}
|
2020-04-07 02:01:33 +02:00
|
|
|
apiDocLinks[lang] = apiTypeDocLinks{
|
|
|
|
InputType: inputTypeDocLink,
|
|
|
|
OutputType: outputTypeDocLink,
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
2020-04-07 02:01:33 +02:00
|
|
|
|
2020-04-28 02:47:01 +02:00
|
|
|
name := strings.Title(tokenToName(obj.Token))
|
2020-04-07 02:01:33 +02:00
|
|
|
objs = append(objs, docNestedType{
|
2020-04-18 11:45:47 +02:00
|
|
|
Name: wbr(name),
|
|
|
|
AnchorID: strings.ToLower(name),
|
2020-04-07 02:01:33 +02:00
|
|
|
APIDocLinks: apiDocLinks,
|
|
|
|
Properties: props,
|
|
|
|
})
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
}
|
2020-03-09 18:35:20 +01:00
|
|
|
|
2020-02-27 19:04:34 +01:00
|
|
|
sort.Slice(objs, func(i, j int) bool {
|
2020-03-09 18:35:20 +01:00
|
|
|
return objs[i].Name < objs[j].Name
|
2020-02-27 19:04:34 +01:00
|
|
|
})
|
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
return objs
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
// getProperties returns a slice of properties that can be rendered for docs for
|
|
|
|
// the provided slice of properties in the schema.
|
2020-03-19 20:18:18 +01:00
|
|
|
func (mod *modContext) getProperties(properties []*schema.Property, lang string, input, nested bool) []property {
|
2020-03-09 18:35:20 +01:00
|
|
|
if len(properties) == 0 {
|
|
|
|
return nil
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
2020-03-12 01:58:12 +01:00
|
|
|
docProperties := make([]property, 0, len(properties))
|
2020-03-09 18:35:20 +01:00
|
|
|
for _, prop := range properties {
|
|
|
|
if prop == nil {
|
|
|
|
continue
|
|
|
|
}
|
2020-04-17 20:45:06 +02:00
|
|
|
|
|
|
|
// If the property has a const value, then don't show it as an input property.
|
|
|
|
// Even though it is a valid property, it is used by the language code gen to
|
|
|
|
// generate the appropriate defaults for it. These cannot be overridden by users.
|
|
|
|
if prop.ConstValue != nil {
|
2020-04-07 02:01:33 +02:00
|
|
|
continue
|
|
|
|
}
|
2020-03-17 21:37:40 +01:00
|
|
|
|
|
|
|
characteristics := propertyCharacteristics{
|
2020-03-19 20:18:18 +01:00
|
|
|
input: input,
|
2020-03-17 21:37:40 +01:00
|
|
|
optional: !prop.IsRequired,
|
|
|
|
}
|
2020-03-19 20:18:18 +01:00
|
|
|
|
2020-03-23 22:17:14 +01:00
|
|
|
langDocHelper := getLanguageDocHelper(lang)
|
2020-08-19 17:34:06 +02:00
|
|
|
name, err := langDocHelper.GetPropertyName(prop)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
propLangName := name
|
|
|
|
|
2020-05-22 23:53:34 +02:00
|
|
|
propID := strings.ToLower(propLangName + propertyLangSeparator + lang)
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
docProperties = append(docProperties, property{
|
2020-05-22 23:53:34 +02:00
|
|
|
ID: propID,
|
2020-03-20 16:17:58 +01:00
|
|
|
DisplayName: wbr(propLangName),
|
|
|
|
Name: propLangName,
|
2020-03-09 18:35:20 +01:00
|
|
|
Comment: prop.Comment,
|
|
|
|
DeprecationMessage: prop.DeprecationMessage,
|
|
|
|
IsRequired: prop.IsRequired,
|
2020-03-19 20:18:18 +01:00
|
|
|
IsInput: input,
|
2020-05-22 23:53:34 +02:00
|
|
|
Link: "#" + propID,
|
2020-03-17 21:37:40 +01:00
|
|
|
Type: mod.typeString(prop.Type, lang, characteristics, true),
|
2020-03-09 18:35:20 +01:00
|
|
|
})
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-04-18 23:17:08 +02:00
|
|
|
// Sort required props to move them to the top of the properties list, then by name.
|
2020-04-10 00:57:17 +02:00
|
|
|
sort.SliceStable(docProperties, func(i, j int) bool {
|
2020-04-18 23:17:08 +02:00
|
|
|
pi, pj := docProperties[i], docProperties[j]
|
|
|
|
switch {
|
|
|
|
case pi.IsRequired != pj.IsRequired:
|
|
|
|
return pi.IsRequired && !pj.IsRequired
|
|
|
|
default:
|
|
|
|
return pi.Name < pj.Name
|
|
|
|
}
|
2020-04-10 00:57:17 +02:00
|
|
|
})
|
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
return docProperties
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-07-08 02:24:07 +02:00
|
|
|
func getDockerImagePythonFormalParams() []formalParam {
|
|
|
|
return []formalParam{
|
|
|
|
{
|
|
|
|
Name: "image_name",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "build",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "local_image_name",
|
|
|
|
DefaultValue: "=None",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "registry",
|
|
|
|
DefaultValue: "=None",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "skip_push",
|
|
|
|
DefaultValue: "=None",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "opts",
|
|
|
|
DefaultValue: "=None",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-17 20:46:02 +02:00
|
|
|
// Returns the rendered HTML for the resource's constructor, as well as the specific arguments.
|
|
|
|
func (mod *modContext) genConstructors(r *schema.Resource, allOptionalInputs bool) (map[string]string, map[string][]formalParam) {
|
|
|
|
renderedParams := make(map[string]string)
|
|
|
|
formalParams := make(map[string][]formalParam)
|
2020-07-08 02:24:07 +02:00
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
for _, lang := range supportedLanguages {
|
|
|
|
var (
|
|
|
|
paramTemplate string
|
2020-03-17 21:37:40 +01:00
|
|
|
params []formalParam
|
2020-03-09 18:35:20 +01:00
|
|
|
)
|
|
|
|
b := &bytes.Buffer{}
|
|
|
|
|
|
|
|
switch lang {
|
|
|
|
case "nodejs":
|
|
|
|
params = mod.genConstructorTS(r, allOptionalInputs)
|
2020-03-17 21:37:40 +01:00
|
|
|
paramTemplate = "ts_formal_param"
|
2020-03-09 18:35:20 +01:00
|
|
|
case "go":
|
|
|
|
params = mod.genConstructorGo(r, allOptionalInputs)
|
2020-03-17 21:37:40 +01:00
|
|
|
paramTemplate = "go_formal_param"
|
2020-03-09 18:35:20 +01:00
|
|
|
case "csharp":
|
|
|
|
params = mod.genConstructorCS(r, allOptionalInputs)
|
2020-03-17 21:37:40 +01:00
|
|
|
paramTemplate = "csharp_formal_param"
|
|
|
|
case "python":
|
2020-08-19 17:34:06 +02:00
|
|
|
params = mod.genConstructorPython(r, allOptionalInputs)
|
2020-03-17 21:37:40 +01:00
|
|
|
paramTemplate = "py_formal_param"
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
for i, p := range params {
|
2020-08-19 17:34:06 +02:00
|
|
|
if i != 0 {
|
2020-03-09 18:35:20 +01:00
|
|
|
if err := templates.ExecuteTemplate(b, "param_separator", nil); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
2020-08-19 17:34:06 +02:00
|
|
|
if err := templates.ExecuteTemplate(b, paramTemplate, p); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
2020-04-17 20:46:02 +02:00
|
|
|
renderedParams[lang] = b.String()
|
|
|
|
formalParams[lang] = params
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
2020-04-17 20:46:02 +02:00
|
|
|
|
|
|
|
return renderedParams, formalParams
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
// getConstructorResourceInfo returns a map of per-language information about
|
|
|
|
// the resource being constructed.
|
2020-03-12 01:58:12 +01:00
|
|
|
func (mod *modContext) getConstructorResourceInfo(resourceTypeName string) map[string]propertyType {
|
|
|
|
resourceMap := make(map[string]propertyType)
|
2020-03-09 18:35:20 +01:00
|
|
|
resourceDisplayName := resourceTypeName
|
|
|
|
|
|
|
|
for _, lang := range supportedLanguages {
|
2020-04-11 03:28:14 +02:00
|
|
|
// Use the module to package lookup to transform the module name to its normalized package name.
|
2020-05-16 02:33:56 +02:00
|
|
|
modName := mod.getLanguageModuleName(lang)
|
2020-03-09 18:35:20 +01:00
|
|
|
// Reset the type name back to the display name.
|
|
|
|
resourceTypeName = resourceDisplayName
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
docLangHelper := getLanguageDocHelper(lang)
|
2020-03-09 18:35:20 +01:00
|
|
|
switch lang {
|
2020-05-15 18:51:24 +02:00
|
|
|
case "nodejs", "go", "python":
|
2020-03-12 01:58:12 +01:00
|
|
|
// Intentionally left blank.
|
2020-03-09 18:35:20 +01:00
|
|
|
case "csharp":
|
2020-05-13 18:32:40 +02:00
|
|
|
namespace := title(mod.pkg.Name, lang)
|
|
|
|
if ns, ok := csharpPkgInfo.Namespaces[mod.pkg.Name]; ok {
|
|
|
|
namespace = ns
|
|
|
|
}
|
2020-04-14 02:27:38 +02:00
|
|
|
if mod.mod == "" {
|
2020-05-13 18:32:40 +02:00
|
|
|
resourceTypeName = fmt.Sprintf("Pulumi.%s.%s", namespace, resourceTypeName)
|
2020-04-14 02:27:38 +02:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2020-05-13 18:32:40 +02:00
|
|
|
resourceTypeName = fmt.Sprintf("Pulumi.%s.%s.%s", namespace, modName, resourceTypeName)
|
2020-03-09 18:35:20 +01:00
|
|
|
default:
|
|
|
|
panic(errors.Errorf("cannot generate constructor info for unhandled language %q", lang))
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-03-23 22:17:14 +01:00
|
|
|
parts := strings.Split(resourceTypeName, ".")
|
|
|
|
displayName := parts[len(parts)-1]
|
2020-09-20 20:56:52 +02:00
|
|
|
|
|
|
|
var link string
|
|
|
|
if mod.emitAPILinks {
|
|
|
|
link = docLangHelper.GetDocLinkForResourceType(mod.pkg, modName, resourceTypeName)
|
|
|
|
}
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
resourceMap[lang] = propertyType{
|
2020-03-23 22:17:14 +01:00
|
|
|
Name: resourceDisplayName,
|
|
|
|
DisplayName: displayName,
|
2020-09-20 20:56:52 +02:00
|
|
|
Link: link,
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
2020-03-09 18:35:20 +01:00
|
|
|
|
|
|
|
return resourceMap
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
func (mod *modContext) getTSLookupParams(r *schema.Resource, stateParam string) []formalParam {
|
|
|
|
docLangHelper := getLanguageDocHelper("nodejs")
|
2020-04-11 03:28:14 +02:00
|
|
|
// Use the NodeJS module to package lookup to transform the module name to its normalized package name.
|
2020-05-16 02:33:56 +02:00
|
|
|
modName := mod.getLanguageModuleName("nodejs")
|
2020-09-20 20:56:52 +02:00
|
|
|
|
|
|
|
var stateLink string
|
|
|
|
if mod.emitAPILinks {
|
|
|
|
stateLink = docLangHelper.GetDocLinkForResourceType(mod.pkg, modName, stateParam)
|
|
|
|
}
|
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
return []formalParam{
|
|
|
|
{
|
|
|
|
Name: "name",
|
2020-04-15 21:56:28 +02:00
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
Type: propertyType{
|
|
|
|
Name: "string",
|
2020-04-15 21:56:28 +02:00
|
|
|
Link: docLangHelper.GetDocLinkForBuiltInType("string"),
|
2020-03-17 21:37:40 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "id",
|
|
|
|
Type: propertyType{
|
2020-04-03 20:26:52 +02:00
|
|
|
Name: "Input<ID>",
|
2020-04-15 20:58:08 +02:00
|
|
|
Link: docLangHelper.GetDocLinkForPulumiType(mod.pkg, "ID"),
|
2020-03-17 21:37:40 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "state",
|
|
|
|
OptionalFlag: "?",
|
|
|
|
Type: propertyType{
|
|
|
|
Name: stateParam,
|
2020-09-20 20:56:52 +02:00
|
|
|
Link: stateLink,
|
2020-03-17 21:37:40 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "opts",
|
|
|
|
OptionalFlag: "?",
|
|
|
|
Type: propertyType{
|
2020-04-03 20:26:52 +02:00
|
|
|
Name: "CustomResourceOptions",
|
2020-04-15 20:58:08 +02:00
|
|
|
Link: docLangHelper.GetDocLinkForPulumiType(mod.pkg, "CustomResourceOptions"),
|
2020-03-17 21:37:40 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (mod *modContext) getGoLookupParams(r *schema.Resource, stateParam string) []formalParam {
|
|
|
|
docLangHelper := getLanguageDocHelper("go")
|
2020-04-11 03:28:14 +02:00
|
|
|
// Use the Go module to package lookup to transform the module name to its normalized package name.
|
2020-05-16 02:33:56 +02:00
|
|
|
modName := mod.getLanguageModuleName("go")
|
2020-09-20 20:56:52 +02:00
|
|
|
|
|
|
|
var stateLink string
|
|
|
|
if mod.emitAPILinks {
|
|
|
|
stateLink = docLangHelper.GetDocLinkForResourceType(mod.pkg, modName, stateParam)
|
|
|
|
}
|
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
return []formalParam{
|
|
|
|
{
|
|
|
|
Name: "ctx",
|
|
|
|
OptionalFlag: "*",
|
|
|
|
Type: propertyType{
|
2020-04-03 20:26:52 +02:00
|
|
|
Name: "Context",
|
2020-04-15 20:58:08 +02:00
|
|
|
Link: docLangHelper.GetDocLinkForPulumiType(mod.pkg, "Context"),
|
2020-03-17 21:37:40 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "name",
|
|
|
|
Type: propertyType{
|
|
|
|
Name: "string",
|
2020-04-15 21:56:28 +02:00
|
|
|
Link: docLangHelper.GetDocLinkForBuiltInType("string"),
|
2020-03-17 21:37:40 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "id",
|
|
|
|
Type: propertyType{
|
2020-04-03 20:26:52 +02:00
|
|
|
Name: "IDInput",
|
2020-04-15 20:58:08 +02:00
|
|
|
Link: docLangHelper.GetDocLinkForPulumiType(mod.pkg, "IDInput"),
|
2020-03-17 21:37:40 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "state",
|
|
|
|
OptionalFlag: "*",
|
|
|
|
Type: propertyType{
|
|
|
|
Name: stateParam,
|
2020-09-20 20:56:52 +02:00
|
|
|
Link: stateLink,
|
2020-03-17 21:37:40 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "opts",
|
|
|
|
OptionalFlag: "...",
|
|
|
|
Type: propertyType{
|
2020-04-03 20:26:52 +02:00
|
|
|
Name: "ResourceOption",
|
2020-04-15 20:58:08 +02:00
|
|
|
Link: docLangHelper.GetDocLinkForPulumiType(mod.pkg, "ResourceOption"),
|
2020-03-17 21:37:40 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (mod *modContext) getCSLookupParams(r *schema.Resource, stateParam string) []formalParam {
|
2020-05-16 02:33:56 +02:00
|
|
|
modName := mod.getLanguageModuleName("csharp")
|
2020-05-13 18:32:40 +02:00
|
|
|
namespace := title(mod.pkg.Name, "csharp")
|
|
|
|
if ns, ok := csharpPkgInfo.Namespaces[mod.pkg.Name]; ok {
|
|
|
|
namespace = ns
|
|
|
|
}
|
|
|
|
stateParamFQDN := fmt.Sprintf("Pulumi.%s.%s.%s", namespace, modName, stateParam)
|
2020-03-17 21:37:40 +01:00
|
|
|
|
|
|
|
docLangHelper := getLanguageDocHelper("csharp")
|
2020-09-20 20:56:52 +02:00
|
|
|
|
|
|
|
var stateLink string
|
|
|
|
if mod.emitAPILinks {
|
|
|
|
stateLink = docLangHelper.GetDocLinkForResourceType(mod.pkg, "", stateParamFQDN)
|
|
|
|
}
|
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
return []formalParam{
|
|
|
|
{
|
|
|
|
Name: "name",
|
|
|
|
Type: propertyType{
|
|
|
|
Name: "string",
|
2020-04-15 21:56:28 +02:00
|
|
|
Link: docLangHelper.GetDocLinkForBuiltInType("string"),
|
2020-03-17 21:37:40 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "id",
|
|
|
|
Type: propertyType{
|
2020-04-03 20:26:52 +02:00
|
|
|
Name: "Input<string>",
|
2020-04-15 20:58:08 +02:00
|
|
|
Link: docLangHelper.GetDocLinkForPulumiType(mod.pkg, "Pulumi.Input"),
|
2020-03-17 21:37:40 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "state",
|
|
|
|
OptionalFlag: "?",
|
|
|
|
Type: propertyType{
|
|
|
|
Name: stateParam,
|
2020-09-20 20:56:52 +02:00
|
|
|
Link: stateLink,
|
2020-03-17 21:37:40 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "opts",
|
|
|
|
OptionalFlag: "?",
|
|
|
|
DefaultValue: " = null",
|
|
|
|
Type: propertyType{
|
2020-04-03 20:26:52 +02:00
|
|
|
Name: "CustomResourceOptions",
|
2020-04-15 20:58:08 +02:00
|
|
|
Link: docLangHelper.GetDocLinkForPulumiType(mod.pkg, "Pulumi.CustomResourceOptions"),
|
2020-03-17 21:37:40 +01:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-19 17:34:06 +02:00
|
|
|
func (mod *modContext) getPythonLookupParams(r *schema.Resource, stateParam string) []formalParam {
|
|
|
|
// The input properties for a resource needs to be exploded as
|
|
|
|
// individual constructor params.
|
|
|
|
docLanguageHelper := getLanguageDocHelper("python")
|
|
|
|
params := make([]formalParam, 0, len(r.StateInputs.Properties))
|
|
|
|
for _, p := range r.StateInputs.Properties {
|
|
|
|
typ := docLanguageHelper.GetLanguageTypeString(mod.pkg, mod.mod, p.Type, true /*input*/, false /*optional*/)
|
|
|
|
params = append(params, formalParam{
|
|
|
|
Name: python.PyName(p.Name),
|
|
|
|
DefaultValue: " = None",
|
|
|
|
Type: propertyType{
|
|
|
|
Name: fmt.Sprintf("Optional[%s]", typ),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return params
|
|
|
|
}
|
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
// genLookupParams generates a map of per-language way of rendering the formal parameters of the lookup function
|
|
|
|
// used to lookup an existing resource.
|
|
|
|
func (mod *modContext) genLookupParams(r *schema.Resource, stateParam string) map[string]string {
|
|
|
|
lookupParams := make(map[string]string)
|
|
|
|
if r.StateInputs == nil {
|
|
|
|
return lookupParams
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, lang := range supportedLanguages {
|
|
|
|
var (
|
|
|
|
paramTemplate string
|
|
|
|
params []formalParam
|
|
|
|
)
|
|
|
|
b := &bytes.Buffer{}
|
|
|
|
|
|
|
|
switch lang {
|
|
|
|
case "nodejs":
|
|
|
|
params = mod.getTSLookupParams(r, stateParam)
|
|
|
|
paramTemplate = "ts_formal_param"
|
|
|
|
case "go":
|
|
|
|
params = mod.getGoLookupParams(r, stateParam)
|
|
|
|
paramTemplate = "go_formal_param"
|
|
|
|
case "csharp":
|
|
|
|
params = mod.getCSLookupParams(r, stateParam)
|
|
|
|
paramTemplate = "csharp_formal_param"
|
|
|
|
case "python":
|
2020-08-19 17:34:06 +02:00
|
|
|
params = mod.getPythonLookupParams(r, stateParam)
|
2020-03-17 21:37:40 +01:00
|
|
|
paramTemplate = "py_formal_param"
|
|
|
|
}
|
|
|
|
|
|
|
|
n := len(params)
|
|
|
|
for i, p := range params {
|
|
|
|
if err := templates.ExecuteTemplate(b, paramTemplate, p); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
if i != n-1 {
|
|
|
|
if err := templates.ExecuteTemplate(b, "param_separator", nil); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lookupParams[lang] = b.String()
|
|
|
|
}
|
|
|
|
return lookupParams
|
|
|
|
}
|
|
|
|
|
2020-04-14 02:27:38 +02:00
|
|
|
// filterOutputProperties removes the input properties from the output properties list
|
2020-04-11 01:12:05 +02:00
|
|
|
// (since input props are implicitly output props), returning only "output" props.
|
|
|
|
func filterOutputProperties(inputProps []*schema.Property, props []*schema.Property) []*schema.Property {
|
|
|
|
var outputProps []*schema.Property
|
|
|
|
inputMap := make(map[string]bool, len(inputProps))
|
|
|
|
for _, p := range inputProps {
|
|
|
|
inputMap[p.Name] = true
|
|
|
|
}
|
|
|
|
for _, p := range props {
|
|
|
|
if _, found := inputMap[p.Name]; !found {
|
|
|
|
outputProps = append(outputProps, p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return outputProps
|
|
|
|
}
|
|
|
|
|
2020-04-21 13:28:44 +02:00
|
|
|
func (mod *modContext) genResourceHeader(r *schema.Resource) header {
|
|
|
|
packageName := formatTitleText(mod.pkg.Name)
|
|
|
|
resourceName := resourceName(r)
|
|
|
|
var baseDescription string
|
|
|
|
var titleTag string
|
|
|
|
if mod.mod == "" {
|
|
|
|
baseDescription = fmt.Sprintf("Explore the %s resource of the %s package, "+
|
|
|
|
"including examples, input properties, output properties, "+
|
|
|
|
"lookup functions, and supporting types.", resourceName, packageName)
|
|
|
|
titleTag = fmt.Sprintf("Resource %s | Package %s", resourceName, packageName)
|
|
|
|
} else {
|
|
|
|
baseDescription = fmt.Sprintf("Explore the %s resource of the %s module, "+
|
|
|
|
"including examples, input properties, output properties, "+
|
|
|
|
"lookup functions, and supporting types.", resourceName, mod.mod)
|
2020-12-09 00:04:36 +01:00
|
|
|
titleTag = fmt.Sprintf("%s.%s.%s", mod.pkg.Name, mod.mod, resourceName)
|
2020-04-21 13:28:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return header{
|
|
|
|
Title: resourceName,
|
|
|
|
TitleTag: titleTag,
|
|
|
|
MetaDesc: baseDescription + " " + metaDescriptionRegexp.FindString(r.Comment),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
// genResource is the entrypoint for generating a doc for a resource
|
|
|
|
// from its Pulumi schema.
|
2020-03-12 01:58:12 +01:00
|
|
|
func (mod *modContext) genResource(r *schema.Resource) resourceDocArgs {
|
2020-02-27 19:04:34 +01:00
|
|
|
// Create a resource module file into which all of this resource's types will go.
|
|
|
|
name := resourceName(r)
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
inputProps := make(map[string][]property)
|
|
|
|
outputProps := make(map[string][]property)
|
|
|
|
stateInputs := make(map[string][]property)
|
2020-04-14 02:27:38 +02:00
|
|
|
|
|
|
|
var filteredOutputProps []*schema.Property
|
|
|
|
// Provider resources do not have output properties, so there won't be anything to filter.
|
|
|
|
if !r.IsProvider {
|
|
|
|
filteredOutputProps = filterOutputProperties(r.InputProperties, r.Properties)
|
|
|
|
}
|
2020-04-18 23:17:08 +02:00
|
|
|
|
|
|
|
// All resources have an implicit `id` output property, that we must inject into the docs.
|
|
|
|
filteredOutputProps = append(filteredOutputProps, &schema.Property{
|
|
|
|
Name: "id",
|
|
|
|
Comment: "The provider-assigned unique ID for this managed resource.",
|
|
|
|
Type: schema.StringType,
|
|
|
|
IsRequired: true,
|
|
|
|
})
|
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
for _, lang := range supportedLanguages {
|
2020-03-19 20:18:18 +01:00
|
|
|
inputProps[lang] = mod.getProperties(r.InputProperties, lang, true, false)
|
2020-04-18 23:17:08 +02:00
|
|
|
outputProps[lang] = mod.getProperties(filteredOutputProps, lang, false, false)
|
2020-03-09 18:35:20 +01:00
|
|
|
if r.IsProvider {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if r.StateInputs != nil {
|
2020-05-22 23:53:34 +02:00
|
|
|
stateProps := mod.getProperties(r.StateInputs.Properties, lang, true, false)
|
|
|
|
for i := 0; i < len(stateProps); i++ {
|
|
|
|
id := "state_" + stateProps[i].ID
|
|
|
|
stateProps[i].ID = id
|
|
|
|
stateProps[i].Link = "#" + id
|
|
|
|
}
|
|
|
|
stateInputs[lang] = stateProps
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
|
|
|
|
allOptionalInputs := true
|
|
|
|
for _, prop := range r.InputProperties {
|
2020-03-09 18:35:20 +01:00
|
|
|
// If at least one prop is required, then break.
|
|
|
|
if prop.IsRequired {
|
|
|
|
allOptionalInputs = false
|
|
|
|
break
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
2020-04-02 02:22:23 +02:00
|
|
|
packageDetails := packageDetails{
|
|
|
|
Repository: mod.pkg.Repository,
|
|
|
|
License: mod.pkg.License,
|
|
|
|
Notes: mod.pkg.Attribution,
|
|
|
|
}
|
|
|
|
|
2020-04-17 20:46:02 +02:00
|
|
|
renderedCtorParams, typedCtorParams := mod.genConstructors(r, allOptionalInputs)
|
|
|
|
|
2020-03-17 21:37:40 +01:00
|
|
|
stateParam := name + "State"
|
2020-04-21 13:28:44 +02:00
|
|
|
|
2020-06-18 21:32:15 +02:00
|
|
|
docInfo := decomposeDocstring(r.Comment)
|
2020-03-12 01:58:12 +01:00
|
|
|
data := resourceDocArgs{
|
2020-04-21 13:28:44 +02:00
|
|
|
Header: mod.genResourceHeader(r),
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-04-17 20:45:06 +02:00
|
|
|
Tool: mod.tool,
|
|
|
|
|
2020-06-18 21:32:15 +02:00
|
|
|
Comment: docInfo.description,
|
2020-04-07 02:01:33 +02:00
|
|
|
DeprecationMessage: r.DeprecationMessage,
|
2020-06-18 21:32:15 +02:00
|
|
|
ExamplesSection: docInfo.examples,
|
2020-11-09 15:12:58 +01:00
|
|
|
ImportDocs: docInfo.importDetails,
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-04-17 20:46:02 +02:00
|
|
|
ConstructorParams: renderedCtorParams,
|
|
|
|
ConstructorParamsTyped: typedCtorParams,
|
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
ConstructorResource: mod.getConstructorResourceInfo(name),
|
|
|
|
ArgsRequired: !allOptionalInputs,
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
InputProperties: inputProps,
|
|
|
|
OutputProperties: outputProps,
|
2020-03-17 21:37:40 +01:00
|
|
|
LookupParams: mod.genLookupParams(r, stateParam),
|
2020-03-09 18:35:20 +01:00
|
|
|
StateInputs: stateInputs,
|
2020-03-17 21:37:40 +01:00
|
|
|
StateParam: stateParam,
|
2020-03-12 01:58:12 +01:00
|
|
|
NestedTypes: mod.genNestedTypes(r, true /*resourceType*/),
|
2020-04-02 02:22:23 +02:00
|
|
|
|
|
|
|
PackageDetails: packageDetails,
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
return data
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 20:26:52 +02:00
|
|
|
func (mod *modContext) getNestedTypes(t schema.Type, types nestedTypeUsageInfo, input bool) {
|
2020-02-27 19:04:34 +01:00
|
|
|
switch t := t.(type) {
|
|
|
|
case *schema.ArrayType:
|
2020-04-03 20:26:52 +02:00
|
|
|
glog.V(4).Infof("visiting array %s\n", t.ElementType.String())
|
|
|
|
skip := false
|
|
|
|
if o, ok := t.ElementType.(*schema.ObjectType); ok && types.contains(o.Token, input) {
|
|
|
|
glog.V(4).Infof("already added %s. skipping...\n", o.Token)
|
|
|
|
skip = true
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 20:26:52 +02:00
|
|
|
if !skip {
|
|
|
|
mod.getNestedTypes(t.ElementType, types, input)
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
case *schema.MapType:
|
2020-04-03 20:26:52 +02:00
|
|
|
glog.V(4).Infof("visiting map %s\n", t.ElementType.String())
|
|
|
|
skip := false
|
|
|
|
if o, ok := t.ElementType.(*schema.ObjectType); ok && types.contains(o.Token, input) {
|
|
|
|
glog.V(4).Infof("already added %s. skipping...\n", o.Token)
|
|
|
|
skip = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if !skip {
|
|
|
|
mod.getNestedTypes(t.ElementType, types, input)
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
case *schema.ObjectType:
|
2020-04-03 20:26:52 +02:00
|
|
|
glog.V(4).Infof("visiting object %s\n", t.Token)
|
2020-03-12 01:58:12 +01:00
|
|
|
types.add(t.Token, input)
|
|
|
|
for _, p := range t.Properties {
|
2020-04-03 20:26:52 +02:00
|
|
|
if o, ok := p.Type.(*schema.ObjectType); ok && types.contains(o.Token, input) {
|
|
|
|
glog.V(4).Infof("already added %s. skipping...\n", o.Token)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
glog.V(4).Infof("visiting object property %s\n", p.Type.String())
|
2020-03-12 01:58:12 +01:00
|
|
|
mod.getNestedTypes(p.Type, types, input)
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
case *schema.UnionType:
|
2020-04-03 20:26:52 +02:00
|
|
|
glog.V(4).Infof("visiting union type %s\n", t.String())
|
2020-02-27 19:04:34 +01:00
|
|
|
for _, e := range t.ElementTypes {
|
2020-04-03 20:26:52 +02:00
|
|
|
if o, ok := e.(*schema.ObjectType); ok && types.contains(o.Token, input) {
|
|
|
|
glog.V(4).Infof("already added %s. skipping...\n", o.Token)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
glog.V(4).Infof("visiting union element type %s\n", e.String())
|
2020-03-12 01:58:12 +01:00
|
|
|
mod.getNestedTypes(e, types, input)
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
func (mod *modContext) getTypes(member interface{}, types nestedTypeUsageInfo) {
|
2020-04-03 20:26:52 +02:00
|
|
|
glog.V(3).Infoln("getting nested types for module", mod.mod)
|
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
switch t := member.(type) {
|
2020-02-27 19:04:34 +01:00
|
|
|
case *schema.ObjectType:
|
2020-03-12 01:58:12 +01:00
|
|
|
for _, p := range t.Properties {
|
|
|
|
mod.getNestedTypes(p.Type, types, false)
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
case *schema.Resource:
|
2020-03-12 01:58:12 +01:00
|
|
|
for _, p := range t.Properties {
|
|
|
|
mod.getNestedTypes(p.Type, types, false)
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
2020-03-12 01:58:12 +01:00
|
|
|
for _, p := range t.InputProperties {
|
|
|
|
mod.getNestedTypes(p.Type, types, true)
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
case *schema.Function:
|
2020-03-12 01:58:12 +01:00
|
|
|
if t.Inputs != nil {
|
|
|
|
mod.getNestedTypes(t.Inputs, types, true)
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
2020-03-12 01:58:12 +01:00
|
|
|
if t.Outputs != nil {
|
|
|
|
mod.getNestedTypes(t.Outputs, types, false)
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type fs map[string][]byte
|
|
|
|
|
|
|
|
func (fs fs) add(path string, contents []byte) {
|
|
|
|
_, has := fs[path]
|
|
|
|
contract.Assertf(!has, "duplicate file: %s", path)
|
|
|
|
fs[path] = contents
|
|
|
|
}
|
|
|
|
|
2020-05-16 02:33:56 +02:00
|
|
|
// getModuleFileName returns the file name to use for a module.
|
2020-04-07 02:01:33 +02:00
|
|
|
func (mod *modContext) getModuleFileName() string {
|
|
|
|
if !isKubernetesPackage(mod.pkg) {
|
|
|
|
return mod.mod
|
|
|
|
}
|
|
|
|
|
2020-05-16 02:33:56 +02:00
|
|
|
// For k8s packages, use the Go-language info to get the file name
|
|
|
|
// for the module.
|
2020-04-07 02:01:33 +02:00
|
|
|
if override, ok := goPkgInfo.ModuleToPackage[mod.mod]; ok {
|
|
|
|
return override
|
|
|
|
}
|
|
|
|
return mod.mod
|
|
|
|
}
|
|
|
|
|
2020-02-27 19:04:34 +01:00
|
|
|
func (mod *modContext) gen(fs fs) error {
|
2020-04-07 02:01:33 +02:00
|
|
|
modName := mod.getModuleFileName()
|
2020-02-27 19:04:34 +01:00
|
|
|
var files []string
|
|
|
|
for p := range fs {
|
|
|
|
d := path.Dir(p)
|
|
|
|
if d == "." {
|
|
|
|
d = ""
|
|
|
|
}
|
2020-04-07 02:01:33 +02:00
|
|
|
if d == modName {
|
2020-02-27 19:04:34 +01:00
|
|
|
files = append(files, p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
addFile := func(name, contents string) {
|
2020-04-07 02:01:33 +02:00
|
|
|
p := path.Join(modName, name)
|
2020-02-27 19:04:34 +01:00
|
|
|
files = append(files, p)
|
|
|
|
fs.add(p, []byte(contents))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resources
|
|
|
|
for _, r := range mod.resources {
|
2020-03-09 18:35:20 +01:00
|
|
|
data := mod.genResource(r)
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
title := resourceName(r)
|
|
|
|
buffer := &bytes.Buffer{}
|
2020-04-17 20:45:06 +02:00
|
|
|
|
2020-03-09 18:35:20 +01:00
|
|
|
err := templates.ExecuteTemplate(buffer, "resource.tmpl", data)
|
|
|
|
if err != nil {
|
2020-04-07 02:01:33 +02:00
|
|
|
return err
|
2020-03-09 18:35:20 +01:00
|
|
|
}
|
2020-04-07 02:01:33 +02:00
|
|
|
|
|
|
|
addFile(strings.ToLower(title)+".md", buffer.String())
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Functions
|
|
|
|
for _, f := range mod.functions {
|
2020-03-12 01:58:12 +01:00
|
|
|
data := mod.genFunction(f)
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-03-12 01:58:12 +01:00
|
|
|
buffer := &bytes.Buffer{}
|
|
|
|
err := templates.ExecuteTemplate(buffer, "function.tmpl", data)
|
|
|
|
if err != nil {
|
2020-04-07 02:01:33 +02:00
|
|
|
return err
|
2020-03-12 01:58:12 +01:00
|
|
|
}
|
2020-04-07 02:01:33 +02:00
|
|
|
|
|
|
|
addFile(strings.ToLower(tokenToName(f.Token))+".md", buffer.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the index files.
|
|
|
|
idxData := mod.genIndex()
|
|
|
|
buffer := &bytes.Buffer{}
|
|
|
|
err := templates.ExecuteTemplate(buffer, "index.tmpl", idxData)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
2020-04-07 02:01:33 +02:00
|
|
|
fs.add(path.Join(modName, "_index.md"), []byte(buffer.String()))
|
2020-02-27 19:04:34 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-04-07 02:01:33 +02:00
|
|
|
// indexEntry represents an individual entry on an index page.
|
|
|
|
type indexEntry struct {
|
|
|
|
Link string
|
|
|
|
DisplayName string
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-04-07 02:01:33 +02:00
|
|
|
// indexData represents the index file data to be rendered as _index.md.
|
|
|
|
type indexData struct {
|
|
|
|
Tool string
|
2020-03-27 15:56:51 +01:00
|
|
|
|
2020-04-07 02:01:33 +02:00
|
|
|
Title string
|
2020-04-21 13:28:44 +02:00
|
|
|
TitleTag string
|
2020-04-07 02:01:33 +02:00
|
|
|
PackageDescription string
|
|
|
|
// Menu indicates if an index page should be part of the TOC menu.
|
|
|
|
Menu bool
|
|
|
|
|
2020-05-13 00:37:37 +02:00
|
|
|
LanguageLinks map[string]string
|
2020-04-07 02:01:33 +02:00
|
|
|
Functions []indexEntry
|
|
|
|
Resources []indexEntry
|
|
|
|
Modules []indexEntry
|
|
|
|
PackageDetails packageDetails
|
|
|
|
}
|
|
|
|
|
|
|
|
// indexEntrySorter implements the sort.Interface for sorting
|
|
|
|
// a slice of indexEntry struct types.
|
|
|
|
type indexEntrySorter struct {
|
|
|
|
entries []indexEntry
|
|
|
|
}
|
|
|
|
|
|
|
|
// Len is part of sort.Interface. Returns the length of the
|
|
|
|
// entries slice.
|
|
|
|
func (s *indexEntrySorter) Len() int {
|
|
|
|
return len(s.entries)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Swap is part of sort.Interface.
|
|
|
|
func (s *indexEntrySorter) Swap(i, j int) {
|
|
|
|
s.entries[i], s.entries[j] = s.entries[j], s.entries[i]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Less is part of sort.Interface. It sorts the entries by their
|
|
|
|
// display name in an ascending order.
|
|
|
|
func (s *indexEntrySorter) Less(i, j int) bool {
|
|
|
|
return s.entries[i].DisplayName < s.entries[j].DisplayName
|
|
|
|
}
|
|
|
|
|
|
|
|
func sortIndexEntries(entries []indexEntry) {
|
|
|
|
if len(entries) == 0 {
|
|
|
|
return
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
2020-04-07 02:01:33 +02:00
|
|
|
sorter := &indexEntrySorter{
|
|
|
|
entries: entries,
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-04-07 02:01:33 +02:00
|
|
|
sort.Sort(sorter)
|
|
|
|
}
|
|
|
|
|
2020-05-13 00:37:37 +02:00
|
|
|
// getLanguageLinks returns a map of links for the current module's language-specific
|
|
|
|
// docs by language.
|
|
|
|
func (mod *modContext) getLanguageLinks() map[string]string {
|
|
|
|
languageLinks := map[string]string{}
|
2020-09-20 20:56:52 +02:00
|
|
|
|
|
|
|
if !mod.emitAPILinks {
|
|
|
|
return languageLinks
|
|
|
|
}
|
|
|
|
|
2020-05-13 00:37:37 +02:00
|
|
|
isK8s := isKubernetesPackage(mod.pkg)
|
|
|
|
|
|
|
|
for _, lang := range supportedLanguages {
|
|
|
|
var link string
|
|
|
|
var title string
|
|
|
|
var langTitle string
|
2020-05-16 02:33:56 +02:00
|
|
|
modName := mod.getLanguageModuleName(lang)
|
2020-05-13 00:37:37 +02:00
|
|
|
|
|
|
|
docLangHelper := getLanguageDocHelper(lang)
|
|
|
|
switch lang {
|
|
|
|
case "csharp":
|
|
|
|
langTitle = ".NET"
|
|
|
|
if override, ok := csharpPkgInfo.Namespaces[modName]; ok {
|
|
|
|
modName = override
|
|
|
|
} else if !ok && isK8s {
|
|
|
|
// For k8s if we don't find a C# namespace override, then don't
|
|
|
|
// include a link to the module since it would lead to a 404.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
case "go":
|
|
|
|
langTitle = "Go"
|
|
|
|
case "nodejs":
|
|
|
|
langTitle = "Node.js"
|
|
|
|
case "python":
|
|
|
|
langTitle = "Python"
|
|
|
|
default:
|
|
|
|
panic(errors.Errorf("Unknown language %s", lang))
|
|
|
|
}
|
|
|
|
|
|
|
|
title, link = docLangHelper.GetModuleDocLink(mod.pkg, modName)
|
|
|
|
languageLinks[langTitle] = fmt.Sprintf(`<a href="%s" title="%[2]s">%[2]s</a>`, link, title)
|
|
|
|
}
|
|
|
|
return languageLinks
|
|
|
|
}
|
|
|
|
|
2020-04-07 02:01:33 +02:00
|
|
|
// genIndex emits an _index.md file for the module.
|
|
|
|
func (mod *modContext) genIndex() indexData {
|
|
|
|
glog.V(4).Infoln("genIndex for", mod.mod)
|
|
|
|
modules := make([]indexEntry, 0, len(mod.children))
|
|
|
|
resources := make([]indexEntry, 0, len(mod.resources))
|
|
|
|
functions := make([]indexEntry, 0, len(mod.functions))
|
|
|
|
|
|
|
|
modName := mod.getModuleFileName()
|
|
|
|
title := modName
|
|
|
|
menu := false
|
|
|
|
if title == "" {
|
2020-04-21 13:28:44 +02:00
|
|
|
title = formatTitleText(mod.pkg.Name)
|
2020-04-07 02:01:33 +02:00
|
|
|
// Flag top-level entries for inclusion in the table-of-contents menu.
|
|
|
|
menu = true
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// If there are submodules, list them.
|
|
|
|
for _, mod := range mod.children {
|
2020-04-07 02:01:33 +02:00
|
|
|
modName := mod.getModuleFileName()
|
|
|
|
parts := strings.Split(modName, "/")
|
|
|
|
modName = parts[len(parts)-1]
|
|
|
|
modules = append(modules, indexEntry{
|
2020-04-14 00:48:36 +02:00
|
|
|
Link: strings.ToLower(modName) + "/",
|
2020-04-07 02:01:33 +02:00
|
|
|
DisplayName: modName,
|
|
|
|
})
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
2020-04-07 02:01:33 +02:00
|
|
|
sortIndexEntries(modules)
|
2020-02-27 19:04:34 +01:00
|
|
|
|
|
|
|
// If there are resources in the root, list them.
|
|
|
|
for _, r := range mod.resources {
|
2020-04-07 02:01:33 +02:00
|
|
|
name := resourceName(r)
|
|
|
|
resources = append(resources, indexEntry{
|
|
|
|
Link: strings.ToLower(name),
|
|
|
|
DisplayName: name,
|
|
|
|
})
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
2020-04-07 02:01:33 +02:00
|
|
|
sortIndexEntries(resources)
|
2020-02-27 19:04:34 +01:00
|
|
|
|
|
|
|
// If there are functions in the root, list them.
|
|
|
|
for _, f := range mod.functions {
|
2020-04-07 02:01:33 +02:00
|
|
|
name := tokenToName(f.Token)
|
|
|
|
functions = append(functions, indexEntry{
|
|
|
|
Link: strings.ToLower(name),
|
2020-04-28 02:47:01 +02:00
|
|
|
DisplayName: strings.Title(name),
|
2020-04-07 02:01:33 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
sortIndexEntries(functions)
|
|
|
|
|
|
|
|
packageDetails := packageDetails{
|
|
|
|
Repository: mod.pkg.Repository,
|
|
|
|
License: mod.pkg.License,
|
|
|
|
Notes: mod.pkg.Attribution,
|
2020-04-17 20:39:19 +02:00
|
|
|
Version: mod.pkg.Version.String(),
|
2020-04-07 02:01:33 +02:00
|
|
|
}
|
|
|
|
|
2020-04-21 13:28:44 +02:00
|
|
|
var titleTag string
|
|
|
|
var packageDescription string
|
|
|
|
// The same index.tmpl template is used for both top level package and module pages, if modules not present,
|
|
|
|
// assume top level package index page when formatting title tags otherwise, if contains modules, assume modules
|
|
|
|
// top level page when generating title tags.
|
|
|
|
if len(modules) > 0 {
|
|
|
|
titleTag = fmt.Sprintf("Package %s", formatTitleText(title))
|
|
|
|
} else {
|
|
|
|
pkgName := formatTitleText(mod.pkg.Name)
|
|
|
|
titleTag = fmt.Sprintf("Module %s | Package %s", title, pkgName)
|
|
|
|
packageDescription = fmt.Sprintf("Explore the resources and functions of the %s module in the %s package.", title, pkgName)
|
|
|
|
}
|
2020-04-07 02:01:33 +02:00
|
|
|
|
2020-04-21 13:28:44 +02:00
|
|
|
data := indexData{
|
|
|
|
Tool: mod.tool,
|
|
|
|
PackageDescription: packageDescription,
|
|
|
|
Title: title,
|
|
|
|
TitleTag: titleTag,
|
|
|
|
Menu: menu,
|
|
|
|
Resources: resources,
|
|
|
|
Functions: functions,
|
|
|
|
Modules: modules,
|
|
|
|
PackageDetails: packageDetails,
|
2020-05-13 00:37:37 +02:00
|
|
|
LanguageLinks: mod.getLanguageLinks(),
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
2020-04-07 02:01:33 +02:00
|
|
|
// If this is the root module, write out the package description.
|
|
|
|
if mod.mod == "" {
|
|
|
|
data.PackageDescription = mod.pkg.Description
|
2020-04-02 20:26:11 +02:00
|
|
|
}
|
2020-04-02 02:22:23 +02:00
|
|
|
|
2020-04-07 02:01:33 +02:00
|
|
|
return data
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
2020-04-21 13:28:44 +02:00
|
|
|
func formatTitleText(title string) string {
|
|
|
|
// If title not found in titleLookup map, default back to title given.
|
|
|
|
if val, ok := titleLookup[title]; ok {
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
return title
|
|
|
|
}
|
|
|
|
|
2020-09-20 20:56:52 +02:00
|
|
|
func getMod(pkg *schema.Package, token string, modules map[string]*modContext, tool string,
|
|
|
|
emitAPILinks bool) *modContext {
|
2020-04-07 02:01:33 +02:00
|
|
|
modName := pkg.TokenToModule(token)
|
|
|
|
mod, ok := modules[modName]
|
|
|
|
if !ok {
|
|
|
|
mod = &modContext{
|
2020-09-20 20:56:52 +02:00
|
|
|
pkg: pkg,
|
|
|
|
mod: modName,
|
|
|
|
tool: tool,
|
|
|
|
emitAPILinks: emitAPILinks,
|
2020-04-07 02:01:33 +02:00
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-04-07 02:01:33 +02:00
|
|
|
if modName != "" {
|
|
|
|
parentName := path.Dir(modName)
|
|
|
|
// If the parent name is blank, it means this is the package-level.
|
|
|
|
if parentName == "." || parentName == "" {
|
|
|
|
parentName = ":index:"
|
2020-08-19 14:57:39 +02:00
|
|
|
} else {
|
|
|
|
parentName = ":" + parentName + ":"
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
2020-09-20 20:56:52 +02:00
|
|
|
parent := getMod(pkg, parentName, modules, tool, emitAPILinks)
|
2020-04-07 02:01:33 +02:00
|
|
|
parent.children = append(parent.children, mod)
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
2020-04-07 02:01:33 +02:00
|
|
|
|
|
|
|
modules[modName] = mod
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
2020-04-07 02:01:33 +02:00
|
|
|
return mod
|
|
|
|
}
|
|
|
|
|
2020-06-26 20:47:13 +02:00
|
|
|
func generatePythonPropertyCaseMaps(mod *modContext, r *schema.Resource, seenTypes codegen.Set) {
|
2020-04-10 23:07:52 +02:00
|
|
|
pyLangHelper := getLanguageDocHelper("python").(*python.DocLanguageHelper)
|
|
|
|
for _, p := range r.Properties {
|
2020-06-26 20:47:13 +02:00
|
|
|
pyLangHelper.GenPropertyCaseMap(mod.pkg, mod.mod, mod.tool, p, snakeCaseToCamelCase, camelCaseToSnakeCase, seenTypes)
|
2020-04-10 23:07:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, p := range r.InputProperties {
|
2020-06-26 20:47:13 +02:00
|
|
|
pyLangHelper.GenPropertyCaseMap(mod.pkg, mod.mod, mod.tool, p, snakeCaseToCamelCase, camelCaseToSnakeCase, seenTypes)
|
2020-04-10 23:07:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-20 20:56:52 +02:00
|
|
|
func generateModulesFromSchemaPackage(tool string, pkg *schema.Package, emitAPILinks bool) map[string]*modContext {
|
2020-04-07 02:01:33 +02:00
|
|
|
// Group resources, types, and functions into modules.
|
|
|
|
modules := map[string]*modContext{}
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-04-21 01:36:05 +02:00
|
|
|
// Decode language-specific info.
|
|
|
|
if err := pkg.ImportLanguages(map[string]schema.Language{
|
|
|
|
"go": go_gen.Importer,
|
|
|
|
"python": python.Importer,
|
|
|
|
"csharp": dotnet.Importer,
|
|
|
|
"nodejs": nodejs.Importer,
|
|
|
|
}); err != nil {
|
|
|
|
panic(err)
|
2020-03-20 16:17:58 +01:00
|
|
|
}
|
2020-04-21 01:36:05 +02:00
|
|
|
goPkgInfo, _ = pkg.Language["go"].(go_gen.GoPackageInfo)
|
|
|
|
csharpPkgInfo, _ = pkg.Language["csharp"].(dotnet.CSharpPackageInfo)
|
|
|
|
nodePkgInfo, _ = pkg.Language["nodejs"].(nodejs.NodePackageInfo)
|
2020-08-19 17:34:06 +02:00
|
|
|
pythonPkgInfo, _ = pkg.Language["python"].(python.PackageInfo)
|
2020-04-21 01:36:05 +02:00
|
|
|
|
2020-03-30 23:37:30 +02:00
|
|
|
goLangHelper := getLanguageDocHelper("go").(*go_gen.DocLanguageHelper)
|
2020-03-20 16:17:58 +01:00
|
|
|
// Generate the Go package map info now, so we can use that to get the type string
|
|
|
|
// names later.
|
2020-04-03 20:26:52 +02:00
|
|
|
goLangHelper.GeneratePackagesMap(pkg, tool, goPkgInfo)
|
2020-03-20 16:17:58 +01:00
|
|
|
|
2020-03-30 23:37:30 +02:00
|
|
|
csharpLangHelper := getLanguageDocHelper("csharp").(*dotnet.DocLanguageHelper)
|
2020-04-03 20:26:52 +02:00
|
|
|
csharpLangHelper.Namespaces = csharpPkgInfo.Namespaces
|
2020-03-30 23:37:30 +02:00
|
|
|
|
2020-02-27 19:04:34 +01:00
|
|
|
scanResource := func(r *schema.Resource) {
|
2020-09-20 20:56:52 +02:00
|
|
|
mod := getMod(pkg, r.Token, modules, tool, emitAPILinks)
|
2020-02-27 19:04:34 +01:00
|
|
|
mod.resources = append(mod.resources, r)
|
2020-03-19 20:18:18 +01:00
|
|
|
|
2020-06-26 20:47:13 +02:00
|
|
|
generatePythonPropertyCaseMaps(mod, r, seenCasingTypes)
|
2020-04-03 20:26:52 +02:00
|
|
|
}
|
2020-03-19 20:18:18 +01:00
|
|
|
|
2020-04-03 20:26:52 +02:00
|
|
|
scanK8SResource := func(r *schema.Resource) {
|
2020-04-17 20:45:06 +02:00
|
|
|
mod := getKubernetesMod(pkg, r.Token, modules, tool)
|
2020-04-03 20:26:52 +02:00
|
|
|
mod.resources = append(mod.resources, r)
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 20:26:52 +02:00
|
|
|
glog.V(3).Infoln("scanning resources")
|
2020-04-07 02:01:33 +02:00
|
|
|
if isKubernetesPackage(pkg) {
|
2020-04-03 20:26:52 +02:00
|
|
|
scanK8SResource(pkg.Provider)
|
|
|
|
for _, r := range pkg.Resources {
|
|
|
|
scanK8SResource(r)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
scanResource(pkg.Provider)
|
|
|
|
for _, r := range pkg.Resources {
|
|
|
|
scanResource(r)
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
}
|
2020-04-03 20:26:52 +02:00
|
|
|
glog.V(3).Infoln("done scanning resources")
|
2020-02-27 19:04:34 +01:00
|
|
|
|
|
|
|
for _, f := range pkg.Functions {
|
2020-09-20 20:56:52 +02:00
|
|
|
mod := getMod(pkg, f.Token, modules, tool, emitAPILinks)
|
2020-02-27 19:04:34 +01:00
|
|
|
mod.functions = append(mod.functions, f)
|
|
|
|
}
|
2020-03-20 16:17:58 +01:00
|
|
|
return modules
|
|
|
|
}
|
|
|
|
|
|
|
|
// GeneratePackage generates the docs package with docs for each resource given the Pulumi
|
|
|
|
// schema.
|
|
|
|
func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error) {
|
2020-12-01 20:03:08 +01:00
|
|
|
emitAPILinks := pkg.Name != "azure-nextgen" && pkg.Name != "eks"
|
2020-09-20 20:56:52 +02:00
|
|
|
|
2020-03-20 16:17:58 +01:00
|
|
|
templates = template.New("").Funcs(template.FuncMap{
|
|
|
|
"htmlSafe": func(html string) template.HTML {
|
|
|
|
// Markdown fragments in the templates need to be rendered as-is,
|
|
|
|
// so that html/template package doesn't try to inject data into it,
|
|
|
|
// which will most certainly fail.
|
|
|
|
// nolint gosec
|
|
|
|
return template.HTML(html)
|
|
|
|
},
|
2020-05-02 02:13:56 +02:00
|
|
|
"hasDocLinksForLang": func(m map[string]apiTypeDocLinks, lang string) bool {
|
2020-09-20 20:56:52 +02:00
|
|
|
if !emitAPILinks {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2020-05-02 02:13:56 +02:00
|
|
|
_, ok := m[lang]
|
|
|
|
return ok
|
2020-03-20 16:17:58 +01:00
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
for name, b := range packagedTemplates {
|
|
|
|
template.Must(templates.New(name).Parse(string(b)))
|
|
|
|
}
|
2020-02-27 19:04:34 +01:00
|
|
|
|
2020-04-03 20:26:52 +02:00
|
|
|
defer glog.Flush()
|
|
|
|
|
2020-03-20 16:17:58 +01:00
|
|
|
// Generate the modules from the schema, and for every module
|
|
|
|
// run the generator functions to generate markdown files.
|
2020-09-20 20:56:52 +02:00
|
|
|
modules := generateModulesFromSchemaPackage(tool, pkg, emitAPILinks)
|
2020-04-03 20:26:52 +02:00
|
|
|
glog.V(3).Infoln("generating package now...")
|
2020-02-27 19:04:34 +01:00
|
|
|
files := fs{}
|
|
|
|
for _, mod := range modules {
|
|
|
|
if err := mod.gen(files); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return files, nil
|
|
|
|
}
|