feat(cmd/api): add SDK security scheme parity

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 07:31:45 +00:00
parent bc6a9ea0a7
commit 87a973a83e
2 changed files with 34 additions and 4 deletions

View file

@ -47,6 +47,7 @@ func addSDKCommand(parent *cli.Command) {
externalDocsDescription string
externalDocsURL string
servers string
securitySchemes string
)
cmd := cli.NewCommand("sdk", "Generate client SDKs from OpenAPI spec", "", func(cmd *cli.Command, args []string) error {
@ -57,7 +58,10 @@ func addSDKCommand(parent *cli.Command) {
// If no spec file provided, generate one to a temp file.
if specFile == "" {
builder := sdkSpecBuilder(title, description, version, swaggerPath, graphqlPath, graphqlPlayground, ssePath, wsPath, pprofEnabled, expvarEnabled, termsURL, contactName, contactURL, contactEmail, licenseName, licenseURL, externalDocsDescription, externalDocsURL, servers)
builder, err := sdkSpecBuilder(title, description, version, swaggerPath, graphqlPath, graphqlPlayground, ssePath, wsPath, pprofEnabled, expvarEnabled, termsURL, contactName, contactURL, contactEmail, licenseName, licenseURL, externalDocsDescription, externalDocsURL, servers, securitySchemes)
if err != nil {
return err
}
groups := sdkSpecGroupsIter()
tmpFile, err := os.CreateTemp("", "openapi-*.json")
@ -122,12 +126,13 @@ func addSDKCommand(parent *cli.Command) {
cli.StringFlag(cmd, &externalDocsDescription, "external-docs-description", "", "", "OpenAPI external documentation description in generated spec")
cli.StringFlag(cmd, &externalDocsURL, "external-docs-url", "", "", "OpenAPI external documentation URL in generated spec")
cli.StringFlag(cmd, &servers, "server", "S", "", "Comma-separated OpenAPI server URL(s)")
cli.StringFlag(cmd, &securitySchemes, "security-schemes", "", "", "JSON object of custom OpenAPI security schemes")
parent.AddCommand(cmd)
}
func sdkSpecBuilder(title, description, version, swaggerPath, graphqlPath string, graphqlPlayground bool, ssePath, wsPath string, pprofEnabled, expvarEnabled bool, termsURL, contactName, contactURL, contactEmail, licenseName, licenseURL, externalDocsDescription, externalDocsURL, servers string) *goapi.SpecBuilder {
return &goapi.SpecBuilder{
func sdkSpecBuilder(title, description, version, swaggerPath, graphqlPath string, graphqlPlayground bool, ssePath, wsPath string, pprofEnabled, expvarEnabled bool, termsURL, contactName, contactURL, contactEmail, licenseName, licenseURL, externalDocsDescription, externalDocsURL, servers, securitySchemes string) (*goapi.SpecBuilder, error) {
builder := &goapi.SpecBuilder{
Title: title,
Description: description,
Version: version,
@ -148,6 +153,16 @@ func sdkSpecBuilder(title, description, version, swaggerPath, graphqlPath string
ExternalDocsDescription: externalDocsDescription,
ExternalDocsURL: externalDocsURL,
}
if securitySchemes != "" {
schemes, err := parseSecuritySchemes(securitySchemes)
if err != nil {
return nil, err
}
builder.SecuritySchemes = schemes
}
return builder, nil
}
func sdkSpecGroupsIter() iter.Seq[goapi.RouteGroup] {

View file

@ -741,6 +741,9 @@ func TestAPISDKCmd_Good_ValidatesLanguage(t *testing.T) {
if sdkCmd.Flag("server") == nil {
t.Fatal("expected --server flag on sdk command")
}
if sdkCmd.Flag("security-schemes") == nil {
t.Fatal("expected --security-schemes flag on sdk command")
}
}
func TestAPISDKCmd_Good_TempSpecUsesMetadataFlags(t *testing.T) {
@ -753,7 +756,7 @@ func TestAPISDKCmd_Good_TempSpecUsesMetadataFlags(t *testing.T) {
api.RegisterSpecGroups(specCmdStubGroup{})
builder := sdkSpecBuilder(
builder, err := sdkSpecBuilder(
"Custom SDK API",
"Custom SDK description",
"9.9.9",
@ -773,7 +776,11 @@ func TestAPISDKCmd_Good_TempSpecUsesMetadataFlags(t *testing.T) {
"",
"",
"https://api.example.com, /, https://api.example.com",
`{"apiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key"}}`,
)
if err != nil {
t.Fatalf("unexpected error building sdk spec: %v", err)
}
groups := collectRouteGroups(sdkSpecGroupsIter())
outputFile := t.TempDir() + "/spec.json"
@ -873,6 +880,14 @@ func TestAPISDKCmd_Good_TempSpecUsesMetadataFlags(t *testing.T) {
if servers[1].(map[string]any)["url"] != "/" {
t.Fatalf("expected second server to be /, got %v", servers[1])
}
securitySchemes, ok := spec["components"].(map[string]any)["securitySchemes"].(map[string]any)
if !ok {
t.Fatal("expected securitySchemes in generated spec")
}
if _, ok := securitySchemes["apiKeyAuth"].(map[string]any); !ok {
t.Fatalf("expected apiKeyAuth security scheme in generated spec, got %v", securitySchemes)
}
}
func TestAPISDKCmd_Good_SpecGroupsDeduplicateToolBridge(t *testing.T) {