mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
Updated API Code Samples (#24141)
* api: remove PHP code samples * api: remove Curl code samples * api: remove Go code samples * link out to marketplace exclusively for community-built drivers * absolute path to V4_SRC * programmatically extract x-codeSamples * initial batch of examples * Update api/server/main.go Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com> * Update api/server/main.go Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com> * Update api/server/main.go Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com> * updated examples --------- Co-authored-by: Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
This commit is contained in:
188
api/server/main.go
Normal file
188
api/server/main.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
|
||||
"github.com/pb33f/libopenapi"
|
||||
v3high "github.com/pb33f/libopenapi/datamodel/high/v3"
|
||||
"golang.org/x/tools/imports"
|
||||
)
|
||||
|
||||
// exampleText defines the template in which the corresponding ExampleClient4_* body is wrapped.
|
||||
const exampleText = `
|
||||
package main
|
||||
|
||||
import (
|
||||
{{- range .Imports -}}
|
||||
{{- if .}}
|
||||
{{"\t"}}{{.}}
|
||||
{{- else}}
|
||||
{{"\t"}}{{end -}}
|
||||
{{- end}}
|
||||
)
|
||||
|
||||
func main() {
|
||||
{{.Body -}}
|
||||
}`
|
||||
|
||||
func main() {
|
||||
var exampleTmpl = template.Must(template.New("example").Parse(exampleText))
|
||||
|
||||
if len(os.Args) <= 1 {
|
||||
log.Fatal("Expected filename to APIv4 spec as argument")
|
||||
}
|
||||
|
||||
filename := os.Args[1]
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to read %s: %s", filename, err)
|
||||
}
|
||||
|
||||
// Parse the Open APIv4 Spec
|
||||
document, err := libopenapi.NewDocument(data)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse OpenAPI spec: %s", err)
|
||||
}
|
||||
|
||||
v3Model, errors := document.BuildV3Model()
|
||||
if len(errors) > 0 {
|
||||
for i := range errors {
|
||||
log.Printf("error: %s\n", errors[i])
|
||||
}
|
||||
log.Fatalf("cannot create v3 model from document: %d errors reported", len(errors))
|
||||
}
|
||||
|
||||
applyExamples(v3Model, exampleTmpl)
|
||||
|
||||
// Re-render the file with the injected examples.
|
||||
newDocument, _, _, errors := document.RenderAndReload()
|
||||
if len(errors) > 0 {
|
||||
for _, err := range errors {
|
||||
log.Printf("error: %s\n", err)
|
||||
}
|
||||
log.Fatalf("cannot render document: %d errors reported", len(errors))
|
||||
}
|
||||
|
||||
err = os.WriteFile(filename, newDocument, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func applyExamples(v3Model *libopenapi.DocumentModel[v3high.Document], tmpl *template.Template) {
|
||||
fileSet, modelFuncs, err := getModelFuncs()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse example funcs: %s", err)
|
||||
}
|
||||
|
||||
for _, path := range v3Model.Model.Paths.PathItems {
|
||||
applyExample(tmpl, fileSet, modelFuncs, path.Get)
|
||||
applyExample(tmpl, fileSet, modelFuncs, path.Post)
|
||||
applyExample(tmpl, fileSet, modelFuncs, path.Delete)
|
||||
applyExample(tmpl, fileSet, modelFuncs, path.Options)
|
||||
applyExample(tmpl, fileSet, modelFuncs, path.Head)
|
||||
applyExample(tmpl, fileSet, modelFuncs, path.Patch)
|
||||
applyExample(tmpl, fileSet, modelFuncs, path.Trace)
|
||||
}
|
||||
}
|
||||
|
||||
// applyExample looks through the functions in model_test to find an ExampleClient4_* matching the
|
||||
// operation's unique identifier.
|
||||
func applyExample(tmpl *template.Template, fileSet *token.FileSet, exampleFuncs []modelFunc, operation *v3high.Operation) {
|
||||
// Not all of GET, POST, OPTIONS, etc. are defined for each operation.
|
||||
if operation == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var exampleFunction modelFunc
|
||||
var found = false
|
||||
for _, e := range exampleFuncs {
|
||||
if e.FuncDecl.Name.Name == "ExampleClient4_"+operation.OperationId {
|
||||
exampleFunction = e
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
// Find all the imports used by the function so we can re-create a minimal example.
|
||||
var fileImports []string
|
||||
for _, i := range exampleFunction.File.Imports {
|
||||
fileImports = append(fileImports, i.Path.Value)
|
||||
}
|
||||
|
||||
// Render the example body using the template.
|
||||
var body bytes.Buffer
|
||||
err := printer.Fprint(&body, fileSet, exampleFunction.FuncDecl.Body.List)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
data := struct {
|
||||
Imports []string
|
||||
Body string
|
||||
}{
|
||||
fileImports,
|
||||
body.String(),
|
||||
}
|
||||
|
||||
// Process the resulting Go file to get the right indention, minimal set of imports, etc.
|
||||
var unformattedExample bytes.Buffer
|
||||
if err := tmpl.Execute(&unformattedExample, data); err != nil {
|
||||
log.Fatalf("failed to render template: %v", err)
|
||||
}
|
||||
|
||||
ignoredFilePath := "path"
|
||||
example, err := imports.Process(ignoredFilePath, unformattedExample.Bytes(), nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Inject the resulting code sample
|
||||
operation.Extensions["x-codeSamples"] = []struct {
|
||||
Lang string
|
||||
Source string
|
||||
}{
|
||||
{
|
||||
Lang: "Go",
|
||||
Source: string(example),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type modelFunc struct {
|
||||
File *ast.File
|
||||
FuncDecl *ast.FuncDecl
|
||||
}
|
||||
|
||||
// getModelFuncs builds a fileset and function declaration set for the model/model_test packages.
|
||||
func getModelFuncs() (*token.FileSet, []modelFunc, error) {
|
||||
fileSet := token.NewFileSet()
|
||||
packs, err := parser.ParseDir(fileSet, "../../server/public/model", nil, 0)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var examples []modelFunc
|
||||
for _, pack := range packs {
|
||||
for _, f := range pack.Files {
|
||||
for _, d := range f.Decls {
|
||||
if fn, isFn := d.(*ast.FuncDecl); isFn {
|
||||
examples = append(examples, modelFunc{f, fn})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fileSet, examples, nil
|
||||
}
|
||||
Reference in New Issue
Block a user