Summary
An OpenAPI 3.2 document with additionalOperations loses those operations on a parse-and-render round trip. The low-level model parses them, but the high-level PathItem.AdditionalOperations stays nil, GetOperations() omits them, and they are absent from rendered output.
Reproduction
mkdir /tmp/libopenapi-repro && cd /tmp/libopenapi-repro
go mod init repro
go get github.com/pb33f/libopenapi@v0.38.7
cat > main.go <<'EOF'
package main
import (
"encoding/json"
"fmt"
"github.com/pb33f/libopenapi"
)
const spec = `{
"openapi": "3.2.0",
"info": {"title": "Repro", "version": "1.0.0"},
"paths": {"/cache": {
"get": {"summary": "Read the cache"},
"additionalOperations": {"PURGE": {"summary": "Purge the cache"}}
}}
}`
func cacheAdditionalOps(doc []byte) string {
var d struct {
Paths map[string]map[string]json.RawMessage `json:"paths"`
}
json.Unmarshal(doc, &d)
if block, ok := d.Paths["/cache"]["additionalOperations"]; ok {
return string(block)
}
return "(absent)"
}
func main() {
doc, _ := libopenapi.NewDocument([]byte(spec))
model, _ := doc.BuildV3Model()
item, _ := model.Model.Paths.PathItems.Get("/cache")
// The low-level model parsed the additional operations.
fmt.Println("low-level model additionalOperations:")
for pair := item.GoLow().AdditionalOperations.Value.First(); pair != nil; pair = pair.Next() {
fmt.Printf(" %s: %q\n", pair.Key().Value, pair.Value().Value.Summary.Value)
}
// The high-level model never received them.
fmt.Println("\nhigh-level model:")
fmt.Print(" AdditionalOperations:")
if item.AdditionalOperations == nil {
fmt.Print(" nil")
}
for pair := item.AdditionalOperations.First(); pair != nil; pair = pair.Next() {
fmt.Printf(" %s", pair.Key())
}
fmt.Println()
fmt.Print(" Operations returned by GetOperations():")
for pair := item.GetOperations().First(); pair != nil; pair = pair.Next() {
fmt.Printf(" %s", pair.Key())
}
fmt.Println()
rendered, _ := model.Model.RenderJSON(" ")
fmt.Printf("\nrendered document:\n%s\n", rendered)
// What /cache was in the origina doc, against what it rendered back out.
fmt.Printf("\nexpected additionalOperations: %s\n", cacheAdditionalOps([]byte(spec)))
fmt.Printf("actual additionalOperations: %s\n", cacheAdditionalOps(rendered))
}
EOF
go run .
Output — PURGE is parsed, then silently dropped:
low-level model additionalOperations:
PURGE: "Purge the cache"
high-level model:
AdditionalOperations: nil
Operations returned by GetOperations(): get
rendered document:
{
"openapi": "3.2.0",
"info": {
"title": "Repro",
"version": "1.0.0"
},
"paths": {
"/cache": {
"get": {
"summary": "Read the cache"
}
}
}
}
expected additionalOperations: {"PURGE": {"summary": "Purge the cache"}}
actual additionalOperations: (absent)
The PURGE operation is gone from the rendered document, and no error or warning is produced. Replacing the additionalOperations container with a bare "PURGE": {...} key on the path item gives the same result.
Summary
An OpenAPI 3.2 document with
additionalOperationsloses those operations on a parse-and-render round trip. The low-level model parses them, but the high-levelPathItem.AdditionalOperationsstays nil,GetOperations()omits them, and they are absent from rendered output.Reproduction
Output —
PURGEis parsed, then silently dropped:The
PURGEoperation is gone from the rendered document, and no error or warning is produced. Replacing theadditionalOperationscontainer with a bare"PURGE": {...}key on the path item gives the same result.