Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions internal/openapi/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,14 +354,17 @@ func describeBody(op *operationInfo, field string, maxDepth int, names SchemaFla
for _, p := range op.PathParams {
doc.Args = append(doc.Args, SchemaArg{
Name: p.Name,
Placeholder: "<" + slugify(p.Name) + ">",
Placeholder: "<" + canonicalName(p.Name) + ">",
Type: p.Type,
Description: p.Description,
})
}
for _, q := range op.QueryParams {
// Use the same resolution as flag registration so collision renames
// (--param-base-url, --foo-2) are reported exactly as the CLI accepts them.
for _, qf := range resolveQueryFlags(op) {
q := qf.Param
doc.QueryParams = append(doc.QueryParams, SchemaQueryParam{
Flag: "--" + slugify(q.Name),
Flag: "--" + qf.Name,
Name: q.Name,
Type: q.Type,
Enum: q.Enum,
Expand Down
59 changes: 58 additions & 1 deletion internal/openapi/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -622,9 +622,14 @@ func TestSchema_BodylessGetDescribesArgsAndQueryParams(t *testing.T) {
t.Fatalf("args = %v, want 1 entry", doc.Args)
}
arg := doc.Args[0]
if arg.Name != "widgetId" || arg.Placeholder != "<widgetid>" || arg.Type != "string" || arg.Description != "Widget UUID" {
if arg.Name != "widgetId" || arg.Placeholder != "<widget-id>" || arg.Type != "string" || arg.Description != "Widget UUID" {
t.Errorf("arg = %+v, want the widgetId path param", arg)
}
// The placeholder and flag spellings must match what --help and the usage
// line print (canonicalName), not the older bare-lowercase slugify form.
if strings.Contains(arg.Placeholder, "widgetid") {
t.Errorf("placeholder %q uses the old slugify spelling; want kebab-case", arg.Placeholder)
}

byFlag := map[string]SchemaQueryParam{}
for _, q := range doc.QueryParams {
Expand Down Expand Up @@ -1132,3 +1137,55 @@ func TestSchema_StaticDepthLimitMatchesDescriber(t *testing.T) {
t.Errorf("static truncation = %s, describer truncation = %s", got, want)
}
}

// collidingQueryTestSpec has query params whose canonical flag names collide:
// "baseUrl" with the global --base-url, and "branchId"/"branch_id" with each
// other. Flag registration renames these (see resolveQueryFlags); --schema must
// advertise the renamed flags, not the bare canonical spelling.
const collidingQueryTestSpec = `{
"openapi": "3.1.0",
"info": {"title": "test", "version": "1.0"},
"paths": {
"/api/v1/widgets": {
"get": {
"operationId": "widgetsList",
"tags": ["widgets"],
"parameters": [
{"name": "baseUrl", "in": "query", "description": "Filter by URL", "schema": {"type": "string"}},
{"name": "branchId", "in": "query", "description": "Branch (camel)", "schema": {"type": "string"}},
{"name": "branch_id", "in": "query", "description": "Branch (snake)", "schema": {"type": "string"}}
],
"responses": {"200": {"description": "ok"}}
}
}
}
}`

func TestSchema_QueryFlagsReflectCollisionRenames(t *testing.T) {
doc, err := runSchemaCmd(t, collidingQueryTestSpec, "list")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
got := map[string]string{}
for _, q := range doc.QueryParams {
got[q.Name] = q.Flag
}
want := map[string]string{
"baseUrl": "--param-base-url",
"branchId": "--branch-id",
"branch_id": "--branch-id-2",
}
for name, flag := range want {
if got[name] != flag {
t.Errorf("query param %q advertised as %q, want %q (the flag the CLI actually registers)", name, got[name], flag)
}
}

// And the advertised flags must all be accepted by the command itself.
cmd := subcommand(t, collidingQueryTestSpec, "list")
for _, flag := range want {
if cmd.Flags().Lookup(strings.TrimPrefix(flag, "--")) == nil {
t.Errorf("--schema advertises %s but the command does not register it", flag)
}
}
}
Loading