pulumi/pkg/codegen/docs/gen_method.go
2021-08-12 17:20:13 -07:00

330 lines
8.8 KiB
Go

// Copyright 2016-2021, 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"
"sort"
"strings"
"github.com/pulumi/pulumi/pkg/v3/codegen/python"
"github.com/pulumi/pulumi/pkg/v3/codegen/schema"
)
type methodDocArgs struct {
Title string
ResourceName string
DeprecationMessage string
Comment string
ExamplesSection []exampleSection
// MethodName is a map of the language and the method name in that language.
MethodName map[string]string
// MethodArgs is map per language view of the parameters
// in the method.
MethodArgs map[string]string
// MethodResult is a map per language property types
// that is returned as a result of calling a method.
MethodResult map[string]propertyType
// InputProperties is a map per language and the corresponding slice
// of input properties accepted by the method.
InputProperties map[string][]property
// OutputProperties is a map per language and the corresponding slice
// of output properties, which are properties of the MethodResult type.
OutputProperties map[string][]property
}
func (mod *modContext) genMethods(r *schema.Resource) []methodDocArgs {
methods := make([]methodDocArgs, 0, len(r.Methods))
for _, m := range r.Methods {
methods = append(methods, mod.genMethod(r, m))
}
return methods
}
func (mod *modContext) genMethod(r *schema.Resource, m *schema.Method) methodDocArgs {
f := m.Function
inputProps, outputProps := make(map[string][]property), make(map[string][]property)
for _, lang := range supportedLanguages {
if f.Inputs != nil {
exclude := func(name string) bool {
return name == "__self__"
}
props := mod.getPropertiesWithIDPrefixAndExclude(f.Inputs.Properties, lang, true, false, false,
fmt.Sprintf("%s_arg_", m.Name), exclude)
if len(props) > 0 {
inputProps[lang] = props
}
}
if f.Outputs != nil {
outputProps[lang] = mod.getPropertiesWithIDPrefixAndExclude(f.Outputs.Properties, lang, false, false, false,
fmt.Sprintf("%s_result_", m.Name), nil)
}
}
// Generate the per-language map for the method name.
methodNameMap := map[string]string{}
for _, lang := range supportedLanguages {
docHelper := getLanguageDocHelper(lang)
methodNameMap[lang] = docHelper.GetMethodName(m)
}
docInfo := decomposeDocstring(f.Comment)
args := methodDocArgs{
Title: title(m.Name, ""),
ResourceName: resourceName(r),
MethodName: methodNameMap,
MethodArgs: mod.genMethodArgs(r, m, methodNameMap),
MethodResult: mod.getMethodResult(r, m),
Comment: docInfo.description,
DeprecationMessage: f.DeprecationMessage,
ExamplesSection: docInfo.examples,
InputProperties: inputProps,
OutputProperties: outputProps,
}
return args
}
func (mod *modContext) genMethodTS(f *schema.Function, resourceName, methodName string,
optionalArgs bool) []formalParam {
argsType := fmt.Sprintf("%s.%sArgs", resourceName, title(methodName, "nodejs"))
var optionalFlag string
if optionalArgs {
optionalFlag = "?"
}
var params []formalParam
if f.Inputs != nil {
params = append(params, formalParam{
Name: "args",
OptionalFlag: optionalFlag,
Type: propertyType{
Name: argsType,
},
})
}
return params
}
func (mod *modContext) genMethodGo(f *schema.Function, resourceName, methodName string,
optionalArgs bool) []formalParam {
argsType := fmt.Sprintf("%s%sArgs", resourceName, title(methodName, "go"))
params := []formalParam{
{
Name: "ctx",
OptionalFlag: "*",
Type: propertyType{
Name: "Context",
Link: "https://pkg.go.dev/github.com/pulumi/pulumi/sdk/v3/go/pulumi?tab=doc#Context",
},
},
}
var optionalFlag string
if optionalArgs {
optionalFlag = "*"
}
if f.Inputs != nil {
params = append(params, formalParam{
Name: "args",
OptionalFlag: optionalFlag,
Type: propertyType{
Name: argsType,
},
})
}
return params
}
func (mod *modContext) genMethodCS(f *schema.Function, resourceName, methodName string, optionalArgs bool) []formalParam {
argsType := fmt.Sprintf("%s.%sArgs", resourceName, title(methodName, "csharp"))
var params []formalParam
if f.Inputs != nil {
var optionalFlag string
if optionalArgs {
optionalFlag = "?"
}
params = append(params, formalParam{
Name: "args",
OptionalFlag: optionalFlag,
DefaultValue: "",
Type: propertyType{
Name: argsType,
},
})
}
return params
}
func (mod *modContext) genMethodPython(f *schema.Function) []formalParam {
docLanguageHelper := getLanguageDocHelper("python")
var params []formalParam
params = append(params, formalParam{
Name: "self",
})
if f.Inputs != nil {
// Filter out the __self__ argument from the inputs.
args := make([]*schema.Property, 0, len(f.Inputs.InputShape.Properties)-1)
for _, arg := range f.Inputs.InputShape.Properties {
if arg.Name == "__self__" {
continue
}
args = append(args, arg)
}
// Sort required args first.
sort.Slice(args, func(i, j int) bool {
pi, pj := args[i], args[j]
switch {
case pi.IsRequired() != pj.IsRequired():
return pi.IsRequired() && !pj.IsRequired()
default:
return pi.Name < pj.Name
}
})
for _, arg := range args {
typ := docLanguageHelper.GetLanguageTypeString(mod.pkg, mod.mod, arg.Type, true /*input*/)
var defaultValue string
if !arg.IsRequired() {
defaultValue = " = None"
}
params = append(params, formalParam{
Name: python.PyName(arg.Name),
DefaultValue: defaultValue,
Type: propertyType{
Name: typ,
},
})
}
}
return params
}
// genMethodArgs generates the arguments string for a given method that can be
// rendered directly into a template. An empty string indicates no args.
func (mod *modContext) genMethodArgs(r *schema.Resource, m *schema.Method,
methodNameMap map[string]string) map[string]string {
f := m.Function
functionParams := make(map[string]string)
for _, lang := range supportedLanguages {
var (
paramTemplate string
params []formalParam
)
b := &bytes.Buffer{}
paramSeparatorTemplate := "param_separator"
ps := paramSeparator{}
var hasArgs bool
optionalArgs := true
if f.Inputs != nil {
for _, arg := range f.Inputs.InputShape.Properties {
if arg.Name == "__self__" {
continue
}
hasArgs = true
if arg.IsRequired() {
optionalArgs = false
}
}
}
if !hasArgs {
functionParams[lang] = ""
continue
}
switch lang {
case "nodejs":
params = mod.genMethodTS(f, resourceName(r), methodNameMap["nodejs"], optionalArgs)
paramTemplate = "ts_formal_param"
case "go":
params = mod.genMethodGo(f, resourceName(r), methodNameMap["go"], optionalArgs)
paramTemplate = "go_formal_param"
case "csharp":
params = mod.genMethodCS(f, resourceName(r), methodNameMap["csharp"], optionalArgs)
paramTemplate = "csharp_formal_param"
case "python":
params = mod.genMethodPython(f)
paramTemplate = "py_formal_param"
paramSeparatorTemplate = "py_param_separator"
docHelper := getLanguageDocHelper(lang)
methodName := docHelper.GetMethodName(m)
ps = paramSeparator{Indent: strings.Repeat(" ", len("def (")+len(methodName))}
}
n := len(params)
if n == 0 {
functionParams[lang] = ""
continue
}
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, paramSeparatorTemplate, ps); err != nil {
panic(err)
}
}
}
functionParams[lang] = b.String()
}
return functionParams
}
// getMethodResult returns a map of per-language information about the method result.
// An empty propertyType.Name indicates no result.
func (mod *modContext) getMethodResult(r *schema.Resource, m *schema.Method) map[string]propertyType {
resourceMap := make(map[string]propertyType)
var resultTypeName string
for _, lang := range supportedLanguages {
if m.Function.Outputs != nil && len(m.Function.Outputs.Properties) > 0 {
resultTypeName = getLanguageDocHelper(lang).GetMethodResultName(r, m)
}
resourceMap[lang] = propertyType{
Name: resultTypeName,
Link: fmt.Sprintf("#method_%s_result", title(m.Name, "")),
}
}
return resourceMap
}