Files
upstream-mirror 240db4e2a7
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
2026-09-16 00:29:55 +05:00

192 lines
4.8 KiB
Go

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"
"github.com/pb33f/libopenapi/orderedmap"
"go.yaml.in/yaml/v4"
"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, err := document.BuildV3Model()
if err != nil {
log.Fatalf("cannot create v3 model from document: %s", err)
}
applyExamples(v3Model, exampleTmpl)
// Re-render the file with the injected examples.
newDocument, _, _, err := document.RenderAndReload()
if err != nil {
log.Fatalf("cannot render document: %s", err)
}
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.ValuesFromOldest() {
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
type codeSample struct {
Lang string `yaml:"lang"`
Source string `yaml:"source"`
}
yamlBytes, err := yaml.Marshal([]codeSample{{Lang: "Go", Source: string(example)}})
if err != nil {
log.Fatalf("failed to marshal x-codeSamples: %v", err)
}
var samplesNode yaml.Node
if err := yaml.Unmarshal(yamlBytes, &samplesNode); err != nil {
log.Fatalf("failed to create yaml node for x-codeSamples: %v", err)
}
if operation.Extensions == nil {
operation.Extensions = orderedmap.New[string, *yaml.Node]()
}
operation.Extensions.Set("x-codeSamples", samplesNode.Content[0])
}
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
}