In particular, use the parser to filter and extract examples. This also sets up support for entity references in documentation that can be used in order to render language-specific names for resources, functions, types, and properties. Related to #4632 and #4159.
202 lines
5.5 KiB
Go
202 lines
5.5 KiB
Go
package schema
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/url"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/pgavlin/goldmark/ast"
|
|
"github.com/pgavlin/goldmark/testutil"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
var testdataPath = filepath.Join("..", "internal", "test", "testdata")
|
|
|
|
var nodeAssertions = testutil.DefaultNodeAssertions().Union(testutil.NodeAssertions{
|
|
KindShortcode: func(t *testing.T, sourceExpected, sourceActual []byte, expected, actual ast.Node) bool {
|
|
shortcodeExpected, shortcodeActual := expected.(*Shortcode), actual.(*Shortcode)
|
|
return testutil.AssertEqualBytes(t, shortcodeExpected.Name, shortcodeActual.Name)
|
|
},
|
|
})
|
|
|
|
type doc struct {
|
|
entity string
|
|
content string
|
|
}
|
|
|
|
func getDocsForProperty(parent string, p *Property) []doc {
|
|
entity := path.Join(parent, p.Name)
|
|
return []doc{
|
|
{entity: entity + "/description", content: p.Comment},
|
|
{entity: entity + "/deprecationMessage", content: p.DeprecationMessage},
|
|
}
|
|
}
|
|
|
|
func getDocsForObjectType(path string, t *ObjectType) []doc {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
|
|
docs := []doc{{entity: path + "/description", content: t.Comment}}
|
|
for _, p := range t.Properties {
|
|
docs = append(docs, getDocsForProperty(path+"/properties", p)...)
|
|
}
|
|
return docs
|
|
}
|
|
|
|
func getDocsForFunction(f *Function) []doc {
|
|
entity := "#/functions/" + url.PathEscape(f.Token)
|
|
docs := []doc{
|
|
{entity: entity + "/description", content: f.Comment},
|
|
{entity: entity + "/deprecationMessage", content: f.DeprecationMessage},
|
|
}
|
|
docs = append(docs, getDocsForObjectType(entity+"/inputs/properties", f.Inputs)...)
|
|
docs = append(docs, getDocsForObjectType(entity+"/outputs/properties", f.Outputs)...)
|
|
return docs
|
|
}
|
|
|
|
func getDocsForResource(r *Resource, isProvider bool) []doc {
|
|
var entity string
|
|
if isProvider {
|
|
entity = "#/provider"
|
|
} else {
|
|
entity = "#/resources/" + url.PathEscape(r.Token)
|
|
}
|
|
|
|
docs := []doc{
|
|
{entity: entity + "/description", content: r.Comment},
|
|
{entity: entity + "/deprecationMessage", content: r.DeprecationMessage},
|
|
}
|
|
for _, p := range r.InputProperties {
|
|
docs = append(docs, getDocsForProperty(entity+"/inputProperties", p)...)
|
|
}
|
|
for _, p := range r.Properties {
|
|
docs = append(docs, getDocsForProperty(entity+"/properties", p)...)
|
|
}
|
|
docs = append(docs, getDocsForObjectType(entity+"/stateInputs", r.StateInputs)...)
|
|
return docs
|
|
}
|
|
|
|
func getDocsForPackage(pkg *Package) []doc {
|
|
var allDocs []doc
|
|
for _, p := range pkg.Config {
|
|
allDocs = append(allDocs, getDocsForProperty("#/config/variables", p)...)
|
|
}
|
|
for _, f := range pkg.Functions {
|
|
allDocs = append(allDocs, getDocsForFunction(f)...)
|
|
}
|
|
allDocs = append(allDocs, getDocsForResource(pkg.Provider, true)...)
|
|
for _, r := range pkg.Resources {
|
|
allDocs = append(allDocs, getDocsForResource(r, false)...)
|
|
}
|
|
for _, t := range pkg.Types {
|
|
if obj, ok := t.(*ObjectType); ok {
|
|
allDocs = append(allDocs, getDocsForObjectType("#/types", obj)...)
|
|
}
|
|
}
|
|
return allDocs
|
|
}
|
|
|
|
func TestParseAndRenderDocs(t *testing.T) {
|
|
files, err := ioutil.ReadDir(testdataPath)
|
|
if err != nil {
|
|
t.Fatalf("could not read test data: %v", err)
|
|
}
|
|
|
|
for _, f := range files {
|
|
if filepath.Ext(f.Name()) != ".json" {
|
|
continue
|
|
}
|
|
|
|
t.Run(f.Name(), func(t *testing.T) {
|
|
path := filepath.Join(testdataPath, f.Name())
|
|
contents, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("could not read %v: %v", path, err)
|
|
}
|
|
|
|
var spec PackageSpec
|
|
if err = json.Unmarshal(contents, &spec); err != nil {
|
|
t.Fatalf("could not unmarshal package spec: %v", err)
|
|
}
|
|
pkg, err := ImportSpec(spec, nil)
|
|
if err != nil {
|
|
t.Fatalf("could not import package: %v", err)
|
|
}
|
|
|
|
for _, doc := range getDocsForPackage(pkg) {
|
|
t.Run(doc.entity, func(t *testing.T) {
|
|
original := []byte(doc.content)
|
|
expected := ParseDocs(original)
|
|
rendered := []byte(RenderDocsToString(original, expected))
|
|
actual := ParseDocs(rendered)
|
|
if !testutil.AssertSameStructure(t, original, rendered, expected, actual, nodeAssertions) {
|
|
t.Logf("original: %v", doc.content)
|
|
t.Logf("rendered: %v", string(rendered))
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReferenceRenderer(t *testing.T) {
|
|
files, err := ioutil.ReadDir(testdataPath)
|
|
if err != nil {
|
|
t.Fatalf("could not read test data: %v", err)
|
|
}
|
|
|
|
for _, f := range files {
|
|
if filepath.Ext(f.Name()) != ".json" {
|
|
continue
|
|
}
|
|
|
|
t.Run(f.Name(), func(t *testing.T) {
|
|
path := filepath.Join(testdataPath, f.Name())
|
|
contents, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("could not read %v: %v", path, err)
|
|
}
|
|
|
|
var spec PackageSpec
|
|
if err = json.Unmarshal(contents, &spec); err != nil {
|
|
t.Fatalf("could not unmarshal package spec: %v", err)
|
|
}
|
|
pkg, err := ImportSpec(spec, nil)
|
|
if err != nil {
|
|
t.Fatalf("could not import package: %v", err)
|
|
}
|
|
|
|
for _, doc := range getDocsForPackage(pkg) {
|
|
t.Run(doc.entity, func(t *testing.T) {
|
|
text := []byte(fmt.Sprintf("[entity](%s)", doc.entity))
|
|
expected := strings.Replace(doc.entity, "/", "_", -1) + "\n"
|
|
|
|
parsed := ParseDocs(text)
|
|
actual := []byte(RenderDocsToString(text, parsed, WithReferenceRenderer(
|
|
func(r *Renderer, w io.Writer, src []byte, l *ast.Link, enter bool) (ast.WalkStatus, error) {
|
|
if !enter {
|
|
return ast.WalkContinue, nil
|
|
}
|
|
|
|
replaced := bytes.Replace(l.Destination, []byte{'/'}, []byte{'_'}, -1)
|
|
if _, err := r.MarkdownRenderer().Write(w, replaced); err != nil {
|
|
return ast.WalkStop, err
|
|
}
|
|
|
|
return ast.WalkSkipChildren, nil
|
|
})))
|
|
|
|
assert.Equal(t, expected, string(actual))
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|