From 915faea048fedc3bb0948927b02d7eec1be968de Mon Sep 17 00:00:00 2001 From: subomi Date: Sat, 8 Nov 2025 17:18:04 +0000 Subject: [PATCH 1/6] feat: add openapi snip command to remove operations from specs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new `speakeasy openapi snip` command that removes specified operations from OpenAPI specifications and cleans up unused components. Features: - Remove mode: removes specified operations (default) - Keep mode: keeps only specified operations, removes everything else - Supports operation selection by operationId or path:method format - Automatically cleans up unused components after operation removal - Outputs to stdout (for piping) or file Examples: - speakeasy openapi snip --schema spec.yaml --operationId deleteUser - speakeasy openapi snip --schema spec.yaml --operation /users/{id}:DELETE - speakeasy openapi snip --keep --schema spec.yaml --operation /users:GET 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- cmd/openapi/openapi.go | 2 +- cmd/openapi/snip.go | 129 ++++++++++++++++++++++++ go.mod | 16 +-- go.sum | 30 +++--- pkg/transform/snip.go | 196 +++++++++++++++++++++++++++++++++++++ pkg/transform/snip_test.go | 160 ++++++++++++++++++++++++++++++ 6 files changed, 509 insertions(+), 24 deletions(-) create mode 100644 cmd/openapi/snip.go create mode 100644 pkg/transform/snip.go create mode 100644 pkg/transform/snip_test.go diff --git a/cmd/openapi/openapi.go b/cmd/openapi/openapi.go index 8ac9782b8..1b19be059 100644 --- a/cmd/openapi/openapi.go +++ b/cmd/openapi/openapi.go @@ -27,7 +27,7 @@ var OpenAPICmd = &model.CommandGroup{ Short: "Utilities for working with OpenAPI documents", Long: utils.RenderMarkdown(openapiLong), InteractiveMsg: "What do you want to do?", - Commands: []model.Command{openapiLintCmd, openapiDiffCmd, transformCmd}, + Commands: []model.Command{openapiLintCmd, openapiDiffCmd, transformCmd, snipCmd}, } var openapiLintCmd = &model.ExecutableCommand[lint.LintOpenapiFlags]{ diff --git a/cmd/openapi/snip.go b/cmd/openapi/snip.go new file mode 100644 index 000000000..f84c5f9ba --- /dev/null +++ b/cmd/openapi/snip.go @@ -0,0 +1,129 @@ +package openapi + +import ( + "context" + "fmt" + + charm_internal "github.com/speakeasy-api/speakeasy/internal/charm" + "github.com/speakeasy-api/speakeasy/internal/model" + "github.com/speakeasy-api/speakeasy/internal/model/flag" + "github.com/speakeasy-api/speakeasy/internal/utils" + "github.com/speakeasy-api/speakeasy/pkg/transform" +) + +const snipLong = `Remove operations from an OpenAPI specification and clean up unused components. + +This command can operate in two modes: + +**Remove Mode (default):** +Removes the specified operations from the document. + +**Keep Mode (--keep flag):** +Keeps only the specified operations and removes everything else. + +## Specifying Operations + +Operations can be specified in two ways: + +1. **By Operation ID** (using --operationId): + The operationId field from your OpenAPI spec + +2. **By Path and Method** (using --operation): + Format: path:METHOD + Example: /users/{id}:DELETE + +You can specify multiple operations by: +- Using the flag multiple times: --operation /users:GET --operation /users:POST +- Using comma-separated values: --operation /users:GET,/users:POST + +## Examples + +Remove specific operations by operation ID: +` + "```" + ` +speakeasy openapi transform snip --schema ./spec.yaml --operationId deleteUser --operationId adminDebug +` + "```" + ` + +Remove operations by path and method: +` + "```" + ` +speakeasy openapi transform snip --schema ./spec.yaml --operation /users/{id}:DELETE --operation /admin:GET +` + "```" + ` + +Keep only specified operations (remove everything else): +` + "```" + ` +speakeasy openapi transform snip --schema ./spec.yaml --keep --operation /users:GET --operation /users:POST +` + "```" + ` + +Write to a file instead of stdout: +` + "```" + ` +speakeasy openapi transform snip --schema ./spec.yaml --out ./public-spec.yaml --operation /internal:GET +` + "```" + ` + +Pipe to other commands: +` + "```" + ` +speakeasy openapi transform snip --schema ./spec.yaml --operation /debug:GET | speakeasy openapi lint +` + "```" + +type snipFlags struct { + Schema string `json:"schema"` + Out string `json:"out"` + OperationIDs []string `json:"operationId"` + Operations []string `json:"operation"` + Keep bool `json:"keep"` +} + +var snipCmd = &model.ExecutableCommand[snipFlags]{ + Usage: "snip", + Short: "Remove operations from an OpenAPI specification", + Long: utils.RenderMarkdown(snipLong), + Run: runSnip, + Flags: []flag.Flag{ + flag.StringFlag{ + Name: "schema", + Shorthand: "s", + Description: "the OpenAPI schema to process", + Required: true, + AutocompleteFileExtensions: charm_internal.OpenAPIFileExtensions, + }, + flag.StringFlag{ + Name: "out", + Shorthand: "o", + Description: "write to a file instead of stdout", + }, + flag.StringSliceFlag{ + Name: "operationId", + Description: "operation ID to process (can be comma-separated or repeated)", + }, + flag.StringSliceFlag{ + Name: "operation", + Description: "operation as path:method to process (can be comma-separated or repeated)", + }, + flag.BooleanFlag{ + Name: "keep", + Shorthand: "k", + Description: "keep mode: keep specified operations and remove all others", + DefaultValue: false, + }, + }, +} + +func runSnip(ctx context.Context, flags snipFlags) error { + // Validate that at least one operation is specified + if len(flags.OperationIDs) == 0 && len(flags.Operations) == 0 { + return fmt.Errorf("no operations specified; use --operationId or --operation flags") + } + + // Combine all operation specifications into a single list + var allOperations []string + allOperations = append(allOperations, flags.OperationIDs...) + allOperations = append(allOperations, flags.Operations...) + + // Setup output + out, yamlOut, err := setupOutput(ctx, flags.Out) + if err != nil { + return err + } + defer out.Close() + + // Run the snip transform + return transform.Snip(ctx, flags.Schema, allOperations, flags.Keep, yamlOut, out) +} diff --git a/go.mod b/go.mod index 9907c7c78..8c3fe118c 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ replace github.com/pb33f/doctor => github.com/speakeasy-api/doctor v0.20.0-fixva replace github.com/pb33f/libopenapi => github.com/speakeasy-api/libopenapi v0.21.9-fixhiddencomps-fixed +replace github.com/speakeasy-api/openapi => ../openapi + require ( github.com/AlekSi/pointer v1.2.0 github.com/KimMachineGun/automemlimit v0.7.1 @@ -52,8 +54,8 @@ require ( github.com/stretchr/testify v1.11.1 go.uber.org/zap v1.27.0 golang.org/x/sync v0.17.0 - golang.org/x/term v0.34.0 - golang.org/x/text v0.29.0 + golang.org/x/term v0.35.0 + golang.org/x/text v0.30.0 gopkg.in/yaml.v3 v3.0.1 oras.land/oras-go/v2 v2.5.0 ) @@ -249,13 +251,13 @@ require ( go.opentelemetry.io/otel/trace v1.35.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.41.0 // indirect + golang.org/x/crypto v0.42.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect golang.org/x/image v0.30.0 // indirect - golang.org/x/mod v0.27.0 // indirect - golang.org/x/net v0.43.0 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/tools v0.36.0 // indirect + golang.org/x/mod v0.28.0 // indirect + golang.org/x/net v0.44.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/tools v0.37.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect google.golang.org/grpc v1.69.4 // indirect diff --git a/go.sum b/go.sum index 688bbdbf2..b6cfabce9 100644 --- a/go.sum +++ b/go.sum @@ -551,8 +551,6 @@ github.com/speakeasy-api/jsonpath v0.6.2 h1:Mys71yd6u8kuowNCR0gCVPlVAHCmKtoGXYoA github.com/speakeasy-api/jsonpath v0.6.2/go.mod h1:ymb2iSkyOycmzKwbEAYPJV/yi2rSmvBCLZJcyD+VVWw= github.com/speakeasy-api/libopenapi v0.21.9-fixhiddencomps-fixed h1:PL/kpBY5vkBmwLjg6l7J4amgSrPf3CNWReGNdwrRqJQ= github.com/speakeasy-api/libopenapi v0.21.9-fixhiddencomps-fixed/go.mod h1:Gc8oQkjr2InxwumK0zOBtKN9gIlv9L2VmSVIUk2YxcU= -github.com/speakeasy-api/openapi v1.7.8 h1:GUASBB/AMewFEmONMsavY1xj5uZXCWeC31G3C6YmvZo= -github.com/speakeasy-api/openapi v1.7.8/go.mod h1:fy+CvRcKj+HDU0QNdnyG6UkfJOEjhqCuNC7o4AtLPAk= github.com/speakeasy-api/openapi-generation/v2 v2.743.2 h1:3SFJeaVDtce9oIwWqwC9EjSLyKr3MzD7BAMGnf2umxM= github.com/speakeasy-api/openapi-generation/v2 v2.743.2/go.mod h1:BFcupAsPG9V10q1DGDI+Uslajq2WcYtRFn4IP4vFYSk= github.com/speakeasy-api/openapi-overlay v0.10.3 h1:70een4vwHyslIp796vM+ox6VISClhtXsCjrQNhxwvWs= @@ -690,8 +688,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -702,8 +700,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -717,8 +715,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -763,16 +761,16 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -780,8 +778,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -791,8 +789,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/transform/snip.go b/pkg/transform/snip.go new file mode 100644 index 000000000..01575a363 --- /dev/null +++ b/pkg/transform/snip.go @@ -0,0 +1,196 @@ +package transform + +import ( + "context" + "fmt" + "io" + "os" + "strings" + + "github.com/speakeasy-api/openapi/marshaller" + "github.com/speakeasy-api/openapi/openapi" +) + +// Snip removes specified operations from an OpenAPI document and cleans up unused components. +// +// Operations can be specified by: +// - Operation ID: "getUserById" +// - Path and method: "/users/{id}:GET" +// +// The function supports two modes: +// - Remove mode (keepMode=false): Removes the specified operations +// - Keep mode (keepMode=true): Keeps only the specified operations, removes everything else +func Snip(ctx context.Context, schemaPath string, operations []string, keepMode bool, yamlOut bool, w io.Writer) error { + // Open and read the input file + f, err := os.Open(schemaPath) + if err != nil { + return fmt.Errorf("failed to open schema file: %w", err) + } + defer f.Close() + + // Parse the OpenAPI document using the openapi package + doc, validationErrors, err := openapi.Unmarshal(ctx, f) + if err != nil { + return fmt.Errorf("failed to unmarshal OpenAPI document: %w", err) + } + if doc == nil { + return fmt.Errorf("failed to parse OpenAPI document: document is nil") + } + + // Report validation errors to stderr (if any) + if len(validationErrors) > 0 { + fmt.Fprintf(os.Stderr, "⚠️ Found %d validation error(s) in the document:\n", len(validationErrors)) + for i, validationErr := range validationErrors { + fmt.Fprintf(os.Stderr, " %d. %s\n", i+1, validationErr.Error()) + } + fmt.Fprintln(os.Stderr) + } + + // Parse operation identifiers from the input strings + operationsToProcess, err := parseOperationIdentifiers(operations) + if err != nil { + return err + } + + if len(operationsToProcess) == 0 { + return fmt.Errorf("no operations specified") + } + + // If in keep mode, we need to invert the operation list + // (find all operations, then remove the ones not in the keep list) + if keepMode { + operationsToProcess, err = invertOperationList(ctx, doc, operationsToProcess) + if err != nil { + return err + } + } + + // Perform the snip operation (this also runs Clean() automatically) + removed, err := openapi.Snip(ctx, doc, operationsToProcess) + if err != nil { + return fmt.Errorf("failed to snip operations: %w", err) + } + + // Write success message to stderr so stdout can be piped + if keepMode { + totalOps := removed + len(operations) + fmt.Fprintf(os.Stderr, "✅ Kept %d operation(s), removed %d operation(s)\n", len(operations), removed) + _ = totalOps // Keep compiler happy + } else { + fmt.Fprintf(os.Stderr, "✅ Removed %d operation(s) and cleaned unused components\n", removed) + } + + // Marshal the modified document to the output writer + if err := marshaller.Marshal(ctx, doc, w); err != nil { + return fmt.Errorf("failed to write output: %w", err) + } + + return nil +} + +// parseOperationIdentifiers converts string representations into OperationIdentifier structs. +// Supports two formats: +// - Operation ID: "getUserById" +// - Path and method: "/users/{id}:GET" +func parseOperationIdentifiers(operations []string) ([]openapi.OperationIdentifier, error) { + var identifiers []openapi.OperationIdentifier + + for _, op := range operations { + if op == "" { + continue + } + + // Check if it's in path:method format + if strings.Contains(op, ":") { + parts := strings.SplitN(op, ":", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("invalid operation format: %s (expected path:METHOD or operationId)", op) + } + + path := parts[0] + method := strings.ToUpper(parts[1]) + + if path == "" || method == "" { + return nil, fmt.Errorf("invalid operation format: %s (path and method cannot be empty)", op) + } + + identifiers = append(identifiers, openapi.OperationIdentifier{ + Path: path, + Method: method, + }) + } else { + // Treat as operation ID + identifiers = append(identifiers, openapi.OperationIdentifier{ + OperationID: op, + }) + } + } + + return identifiers, nil +} + +// invertOperationList converts a "keep" list into a "remove" list by collecting +// all operations in the document and removing the ones in the keep list. +func invertOperationList(ctx context.Context, doc *openapi.OpenAPI, keepList []openapi.OperationIdentifier) ([]openapi.OperationIdentifier, error) { + if doc.Paths == nil || doc.Paths.Len() == 0 { + return nil, fmt.Errorf("no operations found in document") + } + + // Build lookup sets for the keep list + keepByID := make(map[string]bool) + keepByPathMethod := make(map[string]bool) + + for _, keep := range keepList { + if keep.OperationID != "" { + keepByID[keep.OperationID] = true + } + if keep.Path != "" && keep.Method != "" { + key := strings.ToUpper(keep.Method) + " " + keep.Path + keepByPathMethod[key] = true + } + } + + // Collect all operations and determine which ones to remove + var operationsToRemove []openapi.OperationIdentifier + + for path, pathItem := range doc.Paths.All() { + if pathItem == nil || pathItem.Object == nil { + continue + } + + for method, operation := range pathItem.Object.All() { + if operation == nil { + continue + } + + // Check if this operation is in the keep list + shouldKeep := false + + // Check by operation ID + opID := operation.GetOperationID() + if opID != "" && keepByID[opID] { + shouldKeep = true + } + + // Check by path and method + key := strings.ToUpper(string(method)) + " " + path + if keepByPathMethod[key] { + shouldKeep = true + } + + // If not in keep list, add to remove list + if !shouldKeep { + operationsToRemove = append(operationsToRemove, openapi.OperationIdentifier{ + Path: path, + Method: strings.ToUpper(string(method)), + }) + } + } + } + + if len(operationsToRemove) == 0 { + return nil, fmt.Errorf("all operations would be kept; nothing to remove") + } + + return operationsToRemove, nil +} diff --git a/pkg/transform/snip_test.go b/pkg/transform/snip_test.go new file mode 100644 index 000000000..81e1c3836 --- /dev/null +++ b/pkg/transform/snip_test.go @@ -0,0 +1,160 @@ +package transform + +import ( + "bytes" + "context" + "testing" + + "github.com/speakeasy-api/openapi/openapi" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSnip_RemoveByOperationID(t *testing.T) { + ctx := context.Background() + + // Create a buffer to store the snipped spec + var buf bytes.Buffer + + // Call Snip to remove operations by operation ID + err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"deletePet", "findPetsByStatus"}, false, true, &buf) + require.NoError(t, err) + + // Parse the snipped spec using the openapi package + snippedDoc, validationErrors, err := openapi.Unmarshal(ctx, &buf) + require.NoError(t, err) + require.NotNil(t, snippedDoc) + require.Empty(t, validationErrors) + + // Check that the operations are removed + require.NotNil(t, snippedDoc.Paths) + + // Check /pet/{petId} path - delete should be removed but get should remain + petPath, ok := snippedDoc.Paths.Get("/pet/{petId}") + require.True(t, ok, "Path /pet/{petId} should exist") + require.NotNil(t, petPath.Object) + + assert.Nil(t, petPath.Object.GetOperation(openapi.HTTPMethodDelete), "DELETE operation should be removed") + assert.NotNil(t, petPath.Object.GetOperation(openapi.HTTPMethodGet), "GET operation should still exist") + + // Check /pet/findByStatus path - should be completely removed + _, ok = snippedDoc.Paths.Get("/pet/findByStatus") + assert.False(t, ok, "Path /pet/findByStatus should be completely removed") +} + +func TestSnip_RemoveByPathMethod(t *testing.T) { + ctx := context.Background() + + var buf bytes.Buffer + + // Remove operations by path:method format + err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"/pet/{petId}:DELETE", "/pet/findByStatus:GET"}, false, true, &buf) + require.NoError(t, err) + + snippedDoc, _, err := openapi.Unmarshal(ctx, &buf) + require.NoError(t, err) + require.NotNil(t, snippedDoc) + + // Verify operations are removed + petPath, ok := snippedDoc.Paths.Get("/pet/{petId}") + require.True(t, ok) + require.NotNil(t, petPath.Object) + + assert.Nil(t, petPath.Object.GetOperation(openapi.HTTPMethodDelete), "DELETE operation should be removed") + assert.NotNil(t, petPath.Object.GetOperation(openapi.HTTPMethodGet), "GET operation should still exist") +} + +func TestSnip_KeepMode(t *testing.T) { + ctx := context.Background() + + var buf bytes.Buffer + + // Keep only specified operations, remove everything else + err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"getPetById"}, true, true, &buf) + require.NoError(t, err) + + snippedDoc, _, err := openapi.Unmarshal(ctx, &buf) + require.NoError(t, err) + require.NotNil(t, snippedDoc) + + // In keep mode, only getPetById should remain + petPath, ok := snippedDoc.Paths.Get("/pet/{petId}") + require.True(t, ok, "Path /pet/{petId} should exist") + require.NotNil(t, petPath.Object) + + assert.NotNil(t, petPath.Object.GetOperation(openapi.HTTPMethodGet), "GET operation should be kept") + assert.Nil(t, petPath.Object.GetOperation(openapi.HTTPMethodDelete), "DELETE operation should be removed") + + // Other paths should be removed + _, ok = snippedDoc.Paths.Get("/pet/findByStatus") + assert.False(t, ok, "Path /pet/findByStatus should be removed") +} + +func TestSnip_MixedOperationFormats(t *testing.T) { + ctx := context.Background() + + var buf bytes.Buffer + + // Mix operation ID and path:method formats + err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"deletePet", "/pet/findByStatus:GET"}, false, true, &buf) + require.NoError(t, err) + + snippedDoc, _, err := openapi.Unmarshal(ctx, &buf) + require.NoError(t, err) + require.NotNil(t, snippedDoc) + + // Both specified operations should be removed + petPath, ok := snippedDoc.Paths.Get("/pet/{petId}") + require.True(t, ok) + assert.Nil(t, petPath.Object.GetOperation(openapi.HTTPMethodDelete)) + + _, ok = snippedDoc.Paths.Get("/pet/findByStatus") + assert.False(t, ok) +} + +func TestSnip_NoOperationsSpecified(t *testing.T) { + ctx := context.Background() + + var buf bytes.Buffer + + // Should error when no operations are specified + err := Snip(ctx, "../../integration/resources/part1.yaml", []string{}, false, true, &buf) + require.Error(t, err) + assert.Contains(t, err.Error(), "no operations specified") +} + +func TestSnip_InvalidOperationFormat(t *testing.T) { + ctx := context.Background() + + var buf bytes.Buffer + + // Should error on operation without method (missing colon) + err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"/pet"}, false, true, &buf) + require.NoError(t, err, "Operation without colon is treated as operation ID, not an error") + + // Should error on empty path or method + buf.Reset() + err = Snip(ctx, "../../integration/resources/part1.yaml", []string{":GET"}, false, true, &buf) + require.Error(t, err) + assert.Contains(t, err.Error(), "invalid operation format") + + buf.Reset() + err = Snip(ctx, "../../integration/resources/part1.yaml", []string{"/pet:"}, false, true, &buf) + require.Error(t, err) + assert.Contains(t, err.Error(), "invalid operation format") +} + +func TestSnip_NonExistentOperation(t *testing.T) { + ctx := context.Background() + + var buf bytes.Buffer + + // Should not error when removing non-existent operations (graceful handling) + err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"nonExistentOperation"}, false, true, &buf) + require.NoError(t, err) + + // Document should still be valid + snippedDoc, _, err := openapi.Unmarshal(ctx, &buf) + require.NoError(t, err) + require.NotNil(t, snippedDoc) +} From f5173908e942fcc683ce6a83ef38de4a864f32b5 Mon Sep 17 00:00:00 2001 From: subomi Date: Sat, 8 Nov 2025 17:26:43 +0000 Subject: [PATCH 2/6] docs: fix command path in snip help examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update all command examples in the snip help documentation from `speakeasy openapi transform snip` to `speakeasy openapi snip` to reflect the correct command path. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- cmd/openapi/snip.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/openapi/snip.go b/cmd/openapi/snip.go index f84c5f9ba..f745bdb40 100644 --- a/cmd/openapi/snip.go +++ b/cmd/openapi/snip.go @@ -40,27 +40,27 @@ You can specify multiple operations by: Remove specific operations by operation ID: ` + "```" + ` -speakeasy openapi transform snip --schema ./spec.yaml --operationId deleteUser --operationId adminDebug +speakeasy openapi snip --schema ./spec.yaml --operationId deleteUser --operationId adminDebug ` + "```" + ` Remove operations by path and method: ` + "```" + ` -speakeasy openapi transform snip --schema ./spec.yaml --operation /users/{id}:DELETE --operation /admin:GET +speakeasy openapi snip --schema ./spec.yaml --operation /users/{id}:DELETE --operation /admin:GET ` + "```" + ` Keep only specified operations (remove everything else): ` + "```" + ` -speakeasy openapi transform snip --schema ./spec.yaml --keep --operation /users:GET --operation /users:POST +speakeasy openapi snip --schema ./spec.yaml --keep --operation /users:GET --operation /users:POST ` + "```" + ` Write to a file instead of stdout: ` + "```" + ` -speakeasy openapi transform snip --schema ./spec.yaml --out ./public-spec.yaml --operation /internal:GET +speakeasy openapi snip --schema ./spec.yaml --out ./public-spec.yaml --operation /internal:GET ` + "```" + ` Pipe to other commands: ` + "```" + ` -speakeasy openapi transform snip --schema ./spec.yaml --operation /debug:GET | speakeasy openapi lint +speakeasy openapi snip --schema ./spec.yaml --operation /debug:GET | speakeasy openapi lint ` + "```" type snipFlags struct { From 141738467514a753499343044c0782b0079dbc69 Mon Sep 17 00:00:00 2001 From: subomi Date: Sat, 8 Nov 2025 18:33:01 +0000 Subject: [PATCH 3/6] refactor: remove unused yamlOut parameter from Snip function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The yamlOut parameter was not being used in the Snip function. The marshaller.Marshal() function from the openapi package automatically handles output format detection, making this parameter unnecessary. Changes: - Removed yamlOut parameter from Snip() function signature - Updated cmd/openapi/snip.go to not pass yamlOut argument - Updated all test calls to remove the yamlOut parameter 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- cmd/openapi/snip.go | 4 ++-- pkg/transform/snip.go | 4 +--- pkg/transform/snip_test.go | 18 +++++++++--------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/cmd/openapi/snip.go b/cmd/openapi/snip.go index f745bdb40..d7324f3be 100644 --- a/cmd/openapi/snip.go +++ b/cmd/openapi/snip.go @@ -118,12 +118,12 @@ func runSnip(ctx context.Context, flags snipFlags) error { allOperations = append(allOperations, flags.Operations...) // Setup output - out, yamlOut, err := setupOutput(ctx, flags.Out) + out, _, err := setupOutput(ctx, flags.Out) if err != nil { return err } defer out.Close() // Run the snip transform - return transform.Snip(ctx, flags.Schema, allOperations, flags.Keep, yamlOut, out) + return transform.Snip(ctx, flags.Schema, allOperations, flags.Keep, out) } diff --git a/pkg/transform/snip.go b/pkg/transform/snip.go index 01575a363..005c08058 100644 --- a/pkg/transform/snip.go +++ b/pkg/transform/snip.go @@ -20,7 +20,7 @@ import ( // The function supports two modes: // - Remove mode (keepMode=false): Removes the specified operations // - Keep mode (keepMode=true): Keeps only the specified operations, removes everything else -func Snip(ctx context.Context, schemaPath string, operations []string, keepMode bool, yamlOut bool, w io.Writer) error { +func Snip(ctx context.Context, schemaPath string, operations []string, keepMode bool, w io.Writer) error { // Open and read the input file f, err := os.Open(schemaPath) if err != nil { @@ -73,9 +73,7 @@ func Snip(ctx context.Context, schemaPath string, operations []string, keepMode // Write success message to stderr so stdout can be piped if keepMode { - totalOps := removed + len(operations) fmt.Fprintf(os.Stderr, "✅ Kept %d operation(s), removed %d operation(s)\n", len(operations), removed) - _ = totalOps // Keep compiler happy } else { fmt.Fprintf(os.Stderr, "✅ Removed %d operation(s) and cleaned unused components\n", removed) } diff --git a/pkg/transform/snip_test.go b/pkg/transform/snip_test.go index 81e1c3836..7386c8cfd 100644 --- a/pkg/transform/snip_test.go +++ b/pkg/transform/snip_test.go @@ -17,7 +17,7 @@ func TestSnip_RemoveByOperationID(t *testing.T) { var buf bytes.Buffer // Call Snip to remove operations by operation ID - err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"deletePet", "findPetsByStatus"}, false, true, &buf) + err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"deletePet", "findPetsByStatus"}, false, &buf) require.NoError(t, err) // Parse the snipped spec using the openapi package @@ -48,7 +48,7 @@ func TestSnip_RemoveByPathMethod(t *testing.T) { var buf bytes.Buffer // Remove operations by path:method format - err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"/pet/{petId}:DELETE", "/pet/findByStatus:GET"}, false, true, &buf) + err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"/pet/{petId}:DELETE", "/pet/findByStatus:GET"}, false, &buf) require.NoError(t, err) snippedDoc, _, err := openapi.Unmarshal(ctx, &buf) @@ -70,7 +70,7 @@ func TestSnip_KeepMode(t *testing.T) { var buf bytes.Buffer // Keep only specified operations, remove everything else - err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"getPetById"}, true, true, &buf) + err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"getPetById"}, true, &buf) require.NoError(t, err) snippedDoc, _, err := openapi.Unmarshal(ctx, &buf) @@ -96,7 +96,7 @@ func TestSnip_MixedOperationFormats(t *testing.T) { var buf bytes.Buffer // Mix operation ID and path:method formats - err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"deletePet", "/pet/findByStatus:GET"}, false, true, &buf) + err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"deletePet", "/pet/findByStatus:GET"}, false, &buf) require.NoError(t, err) snippedDoc, _, err := openapi.Unmarshal(ctx, &buf) @@ -118,7 +118,7 @@ func TestSnip_NoOperationsSpecified(t *testing.T) { var buf bytes.Buffer // Should error when no operations are specified - err := Snip(ctx, "../../integration/resources/part1.yaml", []string{}, false, true, &buf) + err := Snip(ctx, "../../integration/resources/part1.yaml", []string{}, false, &buf) require.Error(t, err) assert.Contains(t, err.Error(), "no operations specified") } @@ -129,17 +129,17 @@ func TestSnip_InvalidOperationFormat(t *testing.T) { var buf bytes.Buffer // Should error on operation without method (missing colon) - err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"/pet"}, false, true, &buf) + err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"/pet"}, false, &buf) require.NoError(t, err, "Operation without colon is treated as operation ID, not an error") // Should error on empty path or method buf.Reset() - err = Snip(ctx, "../../integration/resources/part1.yaml", []string{":GET"}, false, true, &buf) + err = Snip(ctx, "../../integration/resources/part1.yaml", []string{":GET"}, false, &buf) require.Error(t, err) assert.Contains(t, err.Error(), "invalid operation format") buf.Reset() - err = Snip(ctx, "../../integration/resources/part1.yaml", []string{"/pet:"}, false, true, &buf) + err = Snip(ctx, "../../integration/resources/part1.yaml", []string{"/pet:"}, false, &buf) require.Error(t, err) assert.Contains(t, err.Error(), "invalid operation format") } @@ -150,7 +150,7 @@ func TestSnip_NonExistentOperation(t *testing.T) { var buf bytes.Buffer // Should not error when removing non-existent operations (graceful handling) - err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"nonExistentOperation"}, false, true, &buf) + err := Snip(ctx, "../../integration/resources/part1.yaml", []string{"nonExistentOperation"}, false, &buf) require.NoError(t, err) // Document should still be valid From e515adef9d0d3ab15e4ce3dd3b189c9bc1089225 Mon Sep 17 00:00:00 2001 From: subomi Date: Sat, 8 Nov 2025 19:03:03 +0000 Subject: [PATCH 4/6] fix: update go dep --- go.mod | 4 +--- go.sum | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8c3fe118c..2a5f6a952 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,6 @@ replace github.com/pb33f/doctor => github.com/speakeasy-api/doctor v0.20.0-fixva replace github.com/pb33f/libopenapi => github.com/speakeasy-api/libopenapi v0.21.9-fixhiddencomps-fixed -replace github.com/speakeasy-api/openapi => ../openapi - require ( github.com/AlekSi/pointer v1.2.0 github.com/KimMachineGun/automemlimit v0.7.1 @@ -40,7 +38,7 @@ require ( github.com/sethvargo/go-githubactions v1.1.0 github.com/speakeasy-api/huh v1.1.2 github.com/speakeasy-api/jq v0.1.0 - github.com/speakeasy-api/openapi v1.7.8 + github.com/speakeasy-api/openapi v1.11.0 github.com/speakeasy-api/openapi-generation/v2 v2.743.2 github.com/speakeasy-api/openapi-overlay v0.10.3 github.com/speakeasy-api/sdk-gen-config v1.42.0 diff --git a/go.sum b/go.sum index b6cfabce9..f1937b90a 100644 --- a/go.sum +++ b/go.sum @@ -551,6 +551,8 @@ github.com/speakeasy-api/jsonpath v0.6.2 h1:Mys71yd6u8kuowNCR0gCVPlVAHCmKtoGXYoA github.com/speakeasy-api/jsonpath v0.6.2/go.mod h1:ymb2iSkyOycmzKwbEAYPJV/yi2rSmvBCLZJcyD+VVWw= github.com/speakeasy-api/libopenapi v0.21.9-fixhiddencomps-fixed h1:PL/kpBY5vkBmwLjg6l7J4amgSrPf3CNWReGNdwrRqJQ= github.com/speakeasy-api/libopenapi v0.21.9-fixhiddencomps-fixed/go.mod h1:Gc8oQkjr2InxwumK0zOBtKN9gIlv9L2VmSVIUk2YxcU= +github.com/speakeasy-api/openapi v1.11.0 h1:Hzr1zuXDtdJa+YLGw8ZMM6i19V4T3V5L8T5K8JUZ8gg= +github.com/speakeasy-api/openapi v1.11.0/go.mod h1:JnmVyPKhAhwTup7/jKYpnv2B0O2Lm3C+cjpVZKieVvk= github.com/speakeasy-api/openapi-generation/v2 v2.743.2 h1:3SFJeaVDtce9oIwWqwC9EjSLyKr3MzD7BAMGnf2umxM= github.com/speakeasy-api/openapi-generation/v2 v2.743.2/go.mod h1:BFcupAsPG9V10q1DGDI+Uslajq2WcYtRFn4IP4vFYSk= github.com/speakeasy-api/openapi-overlay v0.10.3 h1:70een4vwHyslIp796vM+ox6VISClhtXsCjrQNhxwvWs= From a8bb6a398ec877d8be04f14b0681ba1065667027 Mon Sep 17 00:00:00 2001 From: subomi Date: Sat, 8 Nov 2025 19:04:52 +0000 Subject: [PATCH 5/6] fix: update go dep --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 08340acef..4f84d543f 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/sethvargo/go-githubactions v1.1.0 github.com/speakeasy-api/huh v1.1.2 github.com/speakeasy-api/jq v0.1.0 - github.com/speakeasy-api/openapi v1.7.8 + github.com/speakeasy-api/openapi v1.11.0 github.com/speakeasy-api/openapi-generation/v2 v2.745.2 github.com/speakeasy-api/openapi-overlay v0.10.3 github.com/speakeasy-api/sdk-gen-config v1.42.0 diff --git a/go.sum b/go.sum index 3071c0b94..05b3a2a4d 100644 --- a/go.sum +++ b/go.sum @@ -551,8 +551,8 @@ github.com/speakeasy-api/jsonpath v0.6.2 h1:Mys71yd6u8kuowNCR0gCVPlVAHCmKtoGXYoA github.com/speakeasy-api/jsonpath v0.6.2/go.mod h1:ymb2iSkyOycmzKwbEAYPJV/yi2rSmvBCLZJcyD+VVWw= github.com/speakeasy-api/libopenapi v0.21.9-fixhiddencomps-fixed h1:PL/kpBY5vkBmwLjg6l7J4amgSrPf3CNWReGNdwrRqJQ= github.com/speakeasy-api/libopenapi v0.21.9-fixhiddencomps-fixed/go.mod h1:Gc8oQkjr2InxwumK0zOBtKN9gIlv9L2VmSVIUk2YxcU= -github.com/speakeasy-api/openapi v1.7.8 h1:GUASBB/AMewFEmONMsavY1xj5uZXCWeC31G3C6YmvZo= -github.com/speakeasy-api/openapi v1.7.8/go.mod h1:fy+CvRcKj+HDU0QNdnyG6UkfJOEjhqCuNC7o4AtLPAk= +github.com/speakeasy-api/openapi v1.11.0 h1:Hzr1zuXDtdJa+YLGw8ZMM6i19V4T3V5L8T5K8JUZ8gg= +github.com/speakeasy-api/openapi v1.11.0/go.mod h1:JnmVyPKhAhwTup7/jKYpnv2B0O2Lm3C+cjpVZKieVvk= github.com/speakeasy-api/openapi-generation/v2 v2.745.2 h1:sxnS6HVpaT19UTzBphO322UhbCB/FjtKi3+Uy8Q6Iso= github.com/speakeasy-api/openapi-generation/v2 v2.745.2/go.mod h1:BFcupAsPG9V10q1DGDI+Uslajq2WcYtRFn4IP4vFYSk= github.com/speakeasy-api/openapi-overlay v0.10.3 h1:70een4vwHyslIp796vM+ox6VISClhtXsCjrQNhxwvWs= From 0e99811f4857b761ad67f42ad7da5addd47752af Mon Sep 17 00:00:00 2001 From: subomi Date: Mon, 10 Nov 2025 13:53:39 +0000 Subject: [PATCH 6/6] fix: update go dep --- go.mod | 1 - 1 file changed, 1 deletion(-) diff --git a/go.mod b/go.mod index 4f84d543f..5cc362613 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.25.0 replace github.com/pb33f/doctor => github.com/speakeasy-api/doctor v0.20.0-fixvacuum replace github.com/pb33f/libopenapi => github.com/speakeasy-api/libopenapi v0.21.9-fixhiddencomps-fixed - require ( github.com/AlekSi/pointer v1.2.0 github.com/KimMachineGun/automemlimit v0.7.1