Fix csharp and nodejs doc links generated by resource docs generator (#4237)

* Decode the C# language info from the schema package and set it in the dotnet lang helper for docs. Pass the qualifier for C# property type strings based on whether it is an input or an output property.

* Don't pass a module name for Pulumi core types used in TS constructor and Function params.

* Add tests for generating doc links for nodejs types.

* Add test for confirming input doc link for C# type.

* Fix the C# type name for InvokeOptions.
This commit is contained in:
Praneet Loke 2020-03-30 14:37:30 -07:00 committed by GitHub
parent e6be38e285
commit bdc12b2c98
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 195 additions and 17 deletions

View file

@ -263,7 +263,13 @@ func (mod *modContext) typeString(t schema.Type, lang string, characteristics pr
var parts []string
var displayName string
if lang == "csharp" {
csharpNS := fmt.Sprintf("Pulumi.%s.%s.", strings.Title(mod.pkg.Name), strings.Title(mod.mod))
// C# types can be wrapped in enumerable types such as List<> or Dictionary<>, so we have to
// only replace the namespace string within the < and the >.
qualifier := "Inputs"
if !characteristics.input {
qualifier = "Outputs"
}
csharpNS := fmt.Sprintf("Pulumi.%s.%s.%s.", strings.Title(mod.pkg.Name), strings.Title(mod.mod), qualifier)
displayName = strings.ReplaceAll(langTypeString, csharpNS, "")
} else {
parts = strings.Split(langTypeString, ".")
@ -272,7 +278,7 @@ func (mod *modContext) typeString(t schema.Type, lang string, characteristics pr
// If word-breaks need to be inserted, then the type string
// should be html-encoded first if the language is C# in order
// to avoid confusing the Hugo rendering where the word-break
// to avoid confusing the Hugo rendering where the word-break
// tags are inserted.
if insertWordBreaks {
if lang == "csharp" {
@ -317,7 +323,7 @@ func (mod *modContext) genConstructorTS(r *schema.Resource, argsOptional bool) [
OptionalFlag: "?",
Type: propertyType{
Name: "pulumi.CustomResourceOptions",
Link: docLangHelper.GetDocLinkForResourceType("pulumi", "pulumi", "CustomResourceOptions"),
Link: docLangHelper.GetDocLinkForResourceType("pulumi", "", "CustomResourceOptions"),
},
},
}
@ -1076,6 +1082,15 @@ func (mod *modContext) genIndex(exports []string) string {
return w.String()
}
func decodeLangSpecificInfo(pkg *schema.Package, lang string, obj interface{}) error {
if csharp, ok := pkg.Language[lang]; ok {
if err := json.Unmarshal([]byte(csharp), &obj); err != nil {
return errors.Wrap(err, "decoding csharp package info")
}
}
return nil
}
func generateModulesFromSchemaPackage(tool string, pkg *schema.Package) map[string]*modContext {
// Group resources, types, and functions into modules.
modules := map[string]*modContext{}
@ -1105,17 +1120,24 @@ func generateModulesFromSchemaPackage(tool string, pkg *schema.Package) map[stri
return mod
}
goLangHelper := getLanguageDocHelper("go").(*go_gen.DocLanguageHelper)
// Decode Go-specific language info.
var goInfo go_gen.GoInfo
if golang, ok := pkg.Language["go"]; ok {
if err := json.Unmarshal(golang, &goInfo); err != nil {
panic(fmt.Errorf("decoding go package info: %v", err))
}
if err := decodeLangSpecificInfo(pkg, "go", &goInfo); err != nil {
panic(errors.Wrap(err, "error decoding go language info"))
}
goLangHelper := getLanguageDocHelper("go").(*go_gen.DocLanguageHelper)
// Generate the Go package map info now, so we can use that to get the type string
// names later.
goLangHelper.GeneratePackagesMap(pkg, tool, goInfo)
// Decode C#-specific language info.
var csharpInfo dotnet.CSharpPackageInfo
if err := decodeLangSpecificInfo(pkg, "csharp", &csharpInfo); err != nil {
panic(errors.Wrap(err, "error decoding c# language info"))
}
csharpLangHelper := getLanguageDocHelper("csharp").(*dotnet.DocLanguageHelper)
csharpLangHelper.Namespaces = csharpInfo.Namespaces
pyLangHelper := getLanguageDocHelper("python").(*python.DocLanguageHelper)
types := &modContext{pkg: pkg, mod: "types", tool: tool}

View file

@ -118,7 +118,7 @@ func (mod *modContext) genFunctionTS(f *schema.Function, resourceName string) []
OptionalFlag: "?",
Type: propertyType{
Name: "pulumi.InvokeOptions",
Link: docLangHelper.GetDocLinkForResourceType("pulumi", "pulumi", "InvokeOptions"),
Link: docLangHelper.GetDocLinkForResourceType("pulumi", "", "InvokeOptions"),
},
})
@ -193,13 +193,14 @@ func (mod *modContext) genFunctionCS(f *schema.Function, resourceName string) []
})
}
optsType := "Pulumi.InvokeOptions"
params = append(params, formalParam{
Name: "opts",
OptionalFlag: "?",
DefaultValue: " = null",
Type: propertyType{
Name: "InvokeOptions",
Link: docLangHelper.GetDocLinkForResourceType("", "", "InvokeOptions"),
Name: optsType,
Link: docLangHelper.GetDocLinkForResourceType("", "", optsType),
},
})
return params

View file

@ -24,7 +24,12 @@ import (
)
// DocLanguageHelper is the DotNet-specific implementation of the DocLanguageHelper.
type DocLanguageHelper struct{}
type DocLanguageHelper struct {
// Namespaces is a map of Pulumi schema module names to their
// C# equivalent names, to be used when creating fully-qualified
// property type strings.
Namespaces map[string]string
}
var _ codegen.DocLanguageHelper = DocLanguageHelper{}
@ -53,9 +58,15 @@ func (d DocLanguageHelper) GetLanguageTypeString(pkg *schema.Package, moduleName
typeDetails := map[*schema.ObjectType]*typeDetails{}
mod := &modContext{
pkg: pkg,
mod: moduleName,
typeDetails: typeDetails,
namespaces: d.Namespaces,
}
return mod.typeString(t, "", input, false /*state*/, false /*wrapInput*/, true /*requireInitializers*/, optional)
qualifier := "Inputs"
if !input {
qualifier = "Outputs"
}
return mod.typeString(t, qualifier, input, false /*state*/, false /*wrapInput*/, true /*requireInitializers*/, optional)
}
// GetResourceFunctionResultName returns the name of the result type when a function is used to lookup

View file

@ -0,0 +1,81 @@
// 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.
// nolint: lll
package dotnet
import (
"testing"
"github.com/pulumi/pulumi/pkg/codegen/schema"
"github.com/stretchr/testify/assert"
)
var testPackageSpec = schema.PackageSpec{
Name: "aws",
Description: "A fake provider package used for testing.",
Meta: &schema.MetadataSpec{
ModuleFormat: "(.*)(?:/[^/]*)",
},
Types: map[string]schema.ObjectTypeSpec{
"aws:s3/BucketCorsRule:BucketCorsRule": {
Description: "The resource options object.",
Type: "object",
Properties: map[string]schema.PropertySpec{
"stringProp": {
Description: "A string prop.",
TypeSpec: schema.TypeSpec{
Type: "string",
},
},
},
},
},
Resources: map[string]schema.ResourceSpec{
"aws:s3/bucket:Bucket": {
InputProperties: map[string]schema.PropertySpec{
"corsRules": {
TypeSpec: schema.TypeSpec{
Ref: "#/types/aws:s3/BucketCorsRule:BucketCorsRule",
},
},
},
},
},
}
func TestGetDocLinkForResourceType(t *testing.T) {
d := DocLanguageHelper{}
expected := "/docs/reference/pkg/dotnet/Pulumi.Aws/Pulumi.Aws.S3.Bucket.html"
link := d.GetDocLinkForResourceType("Aws", "doesNotMatter", "Pulumi.Aws.S3.Bucket")
assert.Equal(t, expected, link)
}
func TestGetDocLinkForResourceInputOrOutputType(t *testing.T) {
pkg, err := schema.ImportSpec(testPackageSpec)
assert.NoError(t, err, "could not import the test package spec")
namespaces := map[string]string{
"s3": "S3",
}
d := DocLanguageHelper{
Namespaces: namespaces,
}
expected := "/docs/reference/pkg/dotnet/Pulumi.Aws/Pulumi.Aws.S3.Inputs.BucketCorsRuleArgs.html"
// Generate the type string for the property type and use that to generate the doc link.
propertyType := pkg.Resources[0].InputProperties[0].Type
typeString := d.GetLanguageTypeString(pkg, "S3", propertyType, true, true)
link := d.GetDocLinkForResourceInputOrOutputType("Aws", "doesNotMatter", typeString, true)
assert.Equal(t, expected, link)
}

View file

@ -1203,14 +1203,16 @@ func computePropertyNames(props []*schema.Property, names map[*schema.Property]s
return nil
}
type csharpPackageInfo struct {
// CSharpPackageInfo represents the C# language-specific info at the root
// of the schema.
type CSharpPackageInfo struct {
PackageReferences map[string]string `json:"packageReferences,omitempty"`
Namespaces map[string]string `json:"namespaces,omitempty"`
}
func GeneratePackage(tool string, pkg *schema.Package, extraFiles map[string][]byte) (map[string][]byte, error) {
// Decode csharp-specific info
var info csharpPackageInfo
var info CSharpPackageInfo
if csharp, ok := pkg.Language["csharp"]; ok {
if err := json.Unmarshal([]byte(csharp), &info); err != nil {
return nil, errors.Wrap(err, "decoding csharp package info")

View file

@ -19,6 +19,7 @@
package nodejs
import (
"errors"
"fmt"
"strings"
@ -33,11 +34,18 @@ var _ codegen.DocLanguageHelper = DocLanguageHelper{}
// GetDocLinkForResourceType returns the NodeJS API doc for a type belonging to a resource provider.
func (d DocLanguageHelper) GetDocLinkForResourceType(packageName, modName, typeName string) string {
if packageName == "" && modName == "" {
panic(errors.New("packageName and modName cannot be empty"))
}
var path string
if packageName != "" {
switch {
case packageName != "" && modName != "":
path = fmt.Sprintf("%s/%s", packageName, modName)
} else {
case packageName == "" && modName != "":
path = modName
case packageName != "" && modName == "":
path = packageName
}
typeName = strings.ReplaceAll(typeName, "?", "")
return fmt.Sprintf("/docs/reference/pkg/nodejs/pulumi/%s/#%s", path, typeName)

View file

@ -0,0 +1,53 @@
// 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 nodejs
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestDocLinkGenerationForPulumiTypes(t *testing.T) {
d := DocLanguageHelper{}
t.Run("GenerateCustomResourceOptionsLink", func(t *testing.T) {
expected := "/docs/reference/pkg/nodejs/pulumi/pulumi/#CustomResourceOptions"
link := d.GetDocLinkForResourceType("pulumi", "", "CustomResourceOptions")
assert.Equal(t, expected, link)
})
t.Run("GenerateInvokeOptionsLink", func(t *testing.T) {
expected := "/docs/reference/pkg/nodejs/pulumi/pulumi/#InvokeOptions"
link := d.GetDocLinkForResourceType("pulumi", "", "InvokeOptions")
assert.Equal(t, expected, link)
})
}
func TestGetDocLinkForResourceType(t *testing.T) {
d := DocLanguageHelper{}
expected := "/docs/reference/pkg/nodejs/pulumi/aws/s3/#Bucket"
link := d.GetDocLinkForResourceType("aws", "s3", "Bucket")
assert.Equal(t, expected, link)
}
func TestGetDocLinkForResourceInputOrOutputType(t *testing.T) {
d := DocLanguageHelper{}
expected := "/docs/reference/pkg/nodejs/pulumi/aws/types/input/#BucketCorsRule"
link := d.GetDocLinkForResourceInputOrOutputType("aws", "s3", "BucketCorsRule", true)
assert.Equal(t, expected, link)
}