From fe8dcd8dfe100d6383b79dcafe039ba30c8cba65 Mon Sep 17 00:00:00 2001 From: "mongodb-sage-bot[bot]" <247496174+mongodb-sage-bot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 14:43:16 +0000 Subject: [PATCH 1/7] CLOUDP-428048: Hide authentication from unauthenticated openapi curl examples ## Proposed changes Update the curl code sample generation in `tools/foas/openapi/filter/code_sample.go` so that endpoints which do not require authentication no longer expose authentication flags in their examples. An operation is treated as unauthenticated when it overrides the global security with an empty set (`security: []`). For those endpoints a single `curl` sample is emitted without `--user "${PUBLIC_KEY}:${PRIVATE_KEY}" --digest` (digest) and without `--header "Authorization: Bearer ${ACCESS_TOKEN}"` (service account). For authenticated operations the Service Account and Digest samples are now gated on the security schemes the operation actually supports, so an endpoint that only references one scheme gets only that sample. Operations that do not override the global security keep emitting both samples as before. The request-building portion shared by every curl variant was extracted into a `curlRequestSuffix` helper; the generated output for the existing samples is unchanged. _Jira ticket:_ CLOUDP-428048 ## Checklist - [ ] I have signed the [MongoDB CLA](https://www.mongodb.com/legal/contributor-agreement) - [x] I have added tests that prove my fix is effective or that my feature works ### Changes to Spectral - [ ] I have read the [README](../tools/spectral/README.md) file for Spectral Updates --- tools/foas/openapi/filter/code_sample.go | 108 +++++++--- tools/foas/openapi/filter/code_sample_test.go | 201 ++++++++++++++++++ 2 files changed, 282 insertions(+), 27 deletions(-) diff --git a/tools/foas/openapi/filter/code_sample.go b/tools/foas/openapi/filter/code_sample.go index 0fc824c326..e33716699c 100644 --- a/tools/foas/openapi/filter/code_sample.go +++ b/tools/foas/openapi/filter/code_sample.go @@ -36,6 +36,12 @@ var goSDKTemplate string const codeSampleExtensionName = "x-codeSamples" const atlasCliExtensionName = "x-xgen-atlascli" +// Security scheme names used by the Atlas Admin API to authenticate requests. An operation is +// considered unauthenticated when it overrides the global security (via "security: []") and does +// not reference either of these schemes. +const digestAuthSchemeName = "DigestAuth" +const serviceAccountSchemeName = "ServiceAccounts" + // https://redocly.com/docs-legacy/api-reference-docs/specification-extensions/x-code-samples#x-codesamples type codeSample struct { Lang string `json:"lang,omitempty" yaml:"lang,omitempty"` @@ -76,11 +82,10 @@ func getFileExtension(format string) string { } } -func (f *CodeSampleFilter) newDigestCurlCodeSamplesForOperation(pathName, opMethod, format string) codeSample { - version := apiVersion(f.metadata.targetVersion) - source := "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest --include \\\n " + - "--header \"Accept: application/vnd.atlas." + version + "+" + format + "\" \\\n " - +// curlRequestSuffix builds the request portion of a curl code sample (method, URL and, where +// relevant, the payload) that is shared by every curl variant regardless of the authentication used. +func curlRequestSuffix(pathName, opMethod, format string) string { + var source string switch opMethod { case "GET": source += "-X " + opMethod + " \"https://cloud.mongodb.com" + pathName @@ -90,7 +95,6 @@ func (f *CodeSampleFilter) newDigestCurlCodeSamplesForOperation(pathName, opMeth } else { source += "?pretty=true\"" } - case "DELETE": source += "-X " + opMethod + " \"https://cloud.mongodb.com" + pathName + "\"" case "POST", "PATCH", "PUT": @@ -99,6 +103,15 @@ func (f *CodeSampleFilter) newDigestCurlCodeSamplesForOperation(pathName, opMeth source += "-d " + "'{ }'" } + return source +} + +func (f *CodeSampleFilter) newDigestCurlCodeSamplesForOperation(pathName, opMethod, format string) codeSample { + version := apiVersion(f.metadata.targetVersion) + source := "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest --include \\\n " + + "--header \"Accept: application/vnd.atlas." + version + "+" + format + "\" \\\n " + + curlRequestSuffix(pathName, opMethod, format) + return codeSample{ Lang: "cURL", Label: "curl (Digest)", @@ -109,28 +122,27 @@ func (f *CodeSampleFilter) newDigestCurlCodeSamplesForOperation(pathName, opMeth func (f *CodeSampleFilter) newServiceAccountCurlCodeSamplesForOperation(pathName, opMethod, format string) codeSample { version := apiVersion(f.metadata.targetVersion) source := "curl --include --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n " + - "--header \"Accept: application/vnd.atlas." + version + "+" + format + "\" \\\n " + "--header \"Accept: application/vnd.atlas." + version + "+" + format + "\" \\\n " + + curlRequestSuffix(pathName, opMethod, format) - switch opMethod { - case "GET": - source += "-X " + opMethod + " \"https://cloud.mongodb.com" + pathName - if format == "gzip" { - source += "\" \\\n " - source += "--output \"file_name." + getFileExtension(format) + "\"" - } else { - source += "?pretty=true\"" - } - case "DELETE": - source += "-X " + opMethod + " \"https://cloud.mongodb.com" + pathName + "\"" - case "POST", "PATCH", "PUT": - source += "--header \"Content-Type: application/json\" \\\n " - source += "-X " + opMethod + " \"https://cloud.mongodb.com" + pathName + "\" \\\n " - source += "-d " + "'{ }'" + return codeSample{ + Lang: "cURL", + Label: "curl (Service Accounts)", + Source: source, } +} + +// newUnauthenticatedCurlCodeSamplesForOperation builds a curl code sample without any authentication +// flags, used for endpoints that do not require authentication. +func (f *CodeSampleFilter) newUnauthenticatedCurlCodeSamplesForOperation(pathName, opMethod, format string) codeSample { + version := apiVersion(f.metadata.targetVersion) + source := "curl --include \\\n " + + "--header \"Accept: application/vnd.atlas." + version + "+" + format + "\" \\\n " + + curlRequestSuffix(pathName, opMethod, format) return codeSample{ Lang: "cURL", - Label: "curl (Service Accounts)", + Label: "curl", Source: source, } } @@ -246,14 +258,56 @@ func (f *CodeSampleFilter) includeCodeSamplesForOperation(pathName, opMethod str } supportedFormat := getSupportedFormat(op) - codeSamples = append( - codeSamples, - f.newServiceAccountCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat), - f.newDigestCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat)) + if isUnauthenticatedOperation(op) { + codeSamples = append(codeSamples, f.newUnauthenticatedCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat)) + } else { + if usesServiceAccountAuth(op) { + codeSamples = append(codeSamples, f.newServiceAccountCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat)) + } + if usesDigestAuth(op) { + codeSamples = append(codeSamples, f.newDigestCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat)) + } + } op.Extensions[codeSampleExtensionName] = codeSamples return nil } +// isUnauthenticatedOperation reports whether the operation does not require authentication. An +// operation is unauthenticated when it overrides the global security requirements with an empty set +// (i.e. "security: []"). When the operation does not override the global security (Security is nil), +// it inherits the authenticated global default. +func isUnauthenticatedOperation(op *openapi3.Operation) bool { + return op.Security != nil && len(*op.Security) == 0 +} + +// usesServiceAccountAuth reports whether the operation supports service account authentication. +// When the operation does not override the global security (Security is nil), it inherits the +// authenticated global default, which supports service accounts. +func usesServiceAccountAuth(op *openapi3.Operation) bool { + return operationSupportsSecurityScheme(op, serviceAccountSchemeName) +} + +// usesDigestAuth reports whether the operation supports digest authentication. When the operation +// does not override the global security (Security is nil), it inherits the authenticated global +// default, which supports digest authentication. +func usesDigestAuth(op *openapi3.Operation) bool { + return operationSupportsSecurityScheme(op, digestAuthSchemeName) +} + +func operationSupportsSecurityScheme(op *openapi3.Operation, scheme string) bool { + if op.Security == nil { + return true + } + + for _, requirement := range *op.Security { + if _, ok := requirement[scheme]; ok { + return true + } + } + + return false +} + // getSupportedFormat inspects the response content types of a given OpenAPI operation, // looking for a content type string in the format "application/vnd.atlas.+". // It splits the content type on the '+' character and returns the last part, which represents the supported format (e.g., "json"). diff --git a/tools/foas/openapi/filter/code_sample_test.go b/tools/foas/openapi/filter/code_sample_test.go index f1a9b6f952..1d1ba9db15 100644 --- a/tools/foas/openapi/filter/code_sample_test.go +++ b/tools/foas/openapi/filter/code_sample_test.go @@ -629,6 +629,207 @@ func TestCodeSampleFilter(t *testing.T) { })), }, }, + { + name: "unauthenticated api omits authentication from the curl sample", + version: "preview", + oas: &openapi3.T{ + Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ + Get: &openapi3.Operation{ + OperationID: "testOperationID", + Summary: "testSummary", + Tags: []string{"TestTag"}, + Security: openapi3.NewSecurityRequirements(), + Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ + Content: openapi3.Content{ + "application/vnd.atlas.preview+json": { + Schema: &openapi3.SchemaRef{ + Ref: "#/components/schemas/PaginatedAppUserView", + }, + Extensions: map[string]any{ + "x-gen-version": "preview", + }, + }, + }, + })), + Extensions: map[string]any{ + "x-sunset": "9999-12-31", + }, + }, + })), + }, + expectedOas: &openapi3.T{ + Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ + Get: &openapi3.Operation{ + OperationID: "testOperationID", + Summary: "testSummary", + Tags: []string{"TestTag"}, + Security: openapi3.NewSecurityRequirements(), + Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ + Content: openapi3.Content{ + "application/vnd.atlas.preview+json": { + Schema: &openapi3.SchemaRef{ + Ref: "#/components/schemas/PaginatedAppUserView", + }, + Extensions: map[string]any{ + "x-gen-version": "preview", + }, + }, + }, + })), + Extensions: map[string]any{ + "x-sunset": "9999-12-31", + "x-codeSamples": []codeSample{ + { + Lang: "cURL", + Label: "Atlas CLI", + Source: "atlas api testTag testOperationId --help", + }, + { + Lang: "cURL", + Label: "curl", + Source: "curl --include \\\n " + + "--header \"Accept: application/vnd.atlas.preview+json\" \\\n " + "-X GET \"https://cloud.mongodb.com/test?pretty=true\"", + }, + }, + }, + }, + })), + }, + }, + { + name: "service account only api omits the digest curl sample", + version: "preview", + oas: &openapi3.T{ + Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ + Get: &openapi3.Operation{ + OperationID: "testOperationID", + Summary: "testSummary", + Tags: []string{"TestTag"}, + Security: openapi3.NewSecurityRequirements().With(openapi3.NewSecurityRequirement().Authenticate("ServiceAccounts")), + Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ + Content: openapi3.Content{ + "application/vnd.atlas.preview+json": { + Schema: &openapi3.SchemaRef{ + Ref: "#/components/schemas/PaginatedAppUserView", + }, + Extensions: map[string]any{ + "x-gen-version": "preview", + }, + }, + }, + })), + Extensions: map[string]any{ + "x-sunset": "9999-12-31", + }, + }, + })), + }, + expectedOas: &openapi3.T{ + Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ + Get: &openapi3.Operation{ + OperationID: "testOperationID", + Summary: "testSummary", + Tags: []string{"TestTag"}, + Security: openapi3.NewSecurityRequirements().With(openapi3.NewSecurityRequirement().Authenticate("ServiceAccounts")), + Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ + Content: openapi3.Content{ + "application/vnd.atlas.preview+json": { + Schema: &openapi3.SchemaRef{ + Ref: "#/components/schemas/PaginatedAppUserView", + }, + Extensions: map[string]any{ + "x-gen-version": "preview", + }, + }, + }, + })), + Extensions: map[string]any{ + "x-sunset": "9999-12-31", + "x-codeSamples": []codeSample{ + { + Lang: "cURL", + Label: "Atlas CLI", + Source: "atlas api testTag testOperationId --help", + }, + { + Lang: "cURL", + Label: "curl (Service Accounts)", + Source: "curl --include --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n " + + "--header \"Accept: application/vnd.atlas.preview+json\" \\\n " + "-X GET \"https://cloud.mongodb.com/test?pretty=true\"", + }, + }, + }, + }, + })), + }, + }, + { + name: "digest only api omits the service account curl sample", + version: "preview", + oas: &openapi3.T{ + Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ + Get: &openapi3.Operation{ + OperationID: "testOperationID", + Summary: "testSummary", + Tags: []string{"TestTag"}, + Security: openapi3.NewSecurityRequirements().With(openapi3.NewSecurityRequirement().Authenticate("DigestAuth")), + Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ + Content: openapi3.Content{ + "application/vnd.atlas.preview+json": { + Schema: &openapi3.SchemaRef{ + Ref: "#/components/schemas/PaginatedAppUserView", + }, + Extensions: map[string]any{ + "x-gen-version": "preview", + }, + }, + }, + })), + Extensions: map[string]any{ + "x-sunset": "9999-12-31", + }, + }, + })), + }, + expectedOas: &openapi3.T{ + Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ + Get: &openapi3.Operation{ + OperationID: "testOperationID", + Summary: "testSummary", + Tags: []string{"TestTag"}, + Security: openapi3.NewSecurityRequirements().With(openapi3.NewSecurityRequirement().Authenticate("DigestAuth")), + Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ + Content: openapi3.Content{ + "application/vnd.atlas.preview+json": { + Schema: &openapi3.SchemaRef{ + Ref: "#/components/schemas/PaginatedAppUserView", + }, + Extensions: map[string]any{ + "x-gen-version": "preview", + }, + }, + }, + })), + Extensions: map[string]any{ + "x-sunset": "9999-12-31", + "x-codeSamples": []codeSample{ + { + Lang: "cURL", + Label: "Atlas CLI", + Source: "atlas api testTag testOperationId --help", + }, + { + Lang: "cURL", + Label: "curl (Digest)", + Source: "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest --include \\\n " + + "--header \"Accept: application/vnd.atlas.preview+json\" \\\n " + "-X GET \"https://cloud.mongodb.com/test?pretty=true\"", + }, + }, + }, + }, + })), + }, + }, } for _, tt := range testCases { From 9ce5514d2ed2160f4752884e696c74732997e417 Mon Sep 17 00:00:00 2001 From: Andrea Angiolillo Date: Tue, 28 Jul 2026 12:40:30 +0100 Subject: [PATCH 2/7] fix --- tools/foas/openapi/filter/code_sample.go | 108 ++++------ tools/foas/openapi/filter/code_sample_test.go | 201 ------------------ 2 files changed, 40 insertions(+), 269 deletions(-) diff --git a/tools/foas/openapi/filter/code_sample.go b/tools/foas/openapi/filter/code_sample.go index e33716699c..2f79ddedf8 100644 --- a/tools/foas/openapi/filter/code_sample.go +++ b/tools/foas/openapi/filter/code_sample.go @@ -36,12 +36,6 @@ var goSDKTemplate string const codeSampleExtensionName = "x-codeSamples" const atlasCliExtensionName = "x-xgen-atlascli" -// Security scheme names used by the Atlas Admin API to authenticate requests. An operation is -// considered unauthenticated when it overrides the global security (via "security: []") and does -// not reference either of these schemes. -const digestAuthSchemeName = "DigestAuth" -const serviceAccountSchemeName = "ServiceAccounts" - // https://redocly.com/docs-legacy/api-reference-docs/specification-extensions/x-code-samples#x-codesamples type codeSample struct { Lang string `json:"lang,omitempty" yaml:"lang,omitempty"` @@ -82,10 +76,9 @@ func getFileExtension(format string) string { } } -// curlRequestSuffix builds the request portion of a curl code sample (method, URL and, where -// relevant, the payload) that is shared by every curl variant regardless of the authentication used. -func curlRequestSuffix(pathName, opMethod, format string) string { - var source string +// appendCurlMethodSource appends the HTTP method specific portion of a curl command +// (URL, query parameters, headers and payload) to the provided base source string. +func appendCurlMethodSource(source, pathName, opMethod, format string) string { switch opMethod { case "GET": source += "-X " + opMethod + " \"https://cloud.mongodb.com" + pathName @@ -109,8 +102,9 @@ func curlRequestSuffix(pathName, opMethod, format string) string { func (f *CodeSampleFilter) newDigestCurlCodeSamplesForOperation(pathName, opMethod, format string) codeSample { version := apiVersion(f.metadata.targetVersion) source := "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest --include \\\n " + - "--header \"Accept: application/vnd.atlas." + version + "+" + format + "\" \\\n " + - curlRequestSuffix(pathName, opMethod, format) + "--header \"Accept: application/vnd.atlas." + version + "+" + format + "\" \\\n " + + source = appendCurlMethodSource(source, pathName, opMethod, format) return codeSample{ Lang: "cURL", @@ -119,30 +113,30 @@ func (f *CodeSampleFilter) newDigestCurlCodeSamplesForOperation(pathName, opMeth } } -func (f *CodeSampleFilter) newServiceAccountCurlCodeSamplesForOperation(pathName, opMethod, format string) codeSample { +func (f *CodeSampleFilter) newCurlCodeSamplesForOperation(pathName, opMethod, format string) codeSample { version := apiVersion(f.metadata.targetVersion) - source := "curl --include --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n " + - "--header \"Accept: application/vnd.atlas." + version + "+" + format + "\" \\\n " + - curlRequestSuffix(pathName, opMethod, format) + source := "curl --include \\\n " + + "--header \"Accept: application/vnd.atlas." + version + "+" + format + "\" \\\n " + + source = appendCurlMethodSource(source, pathName, opMethod, format) return codeSample{ Lang: "cURL", - Label: "curl (Service Accounts)", + Label: "curl", Source: source, } } -// newUnauthenticatedCurlCodeSamplesForOperation builds a curl code sample without any authentication -// flags, used for endpoints that do not require authentication. -func (f *CodeSampleFilter) newUnauthenticatedCurlCodeSamplesForOperation(pathName, opMethod, format string) codeSample { +func (f *CodeSampleFilter) newServiceAccountCurlCodeSamplesForOperation(pathName, opMethod, format string) codeSample { version := apiVersion(f.metadata.targetVersion) - source := "curl --include \\\n " + - "--header \"Accept: application/vnd.atlas." + version + "+" + format + "\" \\\n " + - curlRequestSuffix(pathName, opMethod, format) + source := "curl --include --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n " + + "--header \"Accept: application/vnd.atlas." + version + "+" + format + "\" \\\n " + + source = appendCurlMethodSource(source, pathName, opMethod, format) return codeSample{ Lang: "cURL", - Label: "curl", + Label: "curl (Service Accounts)", Source: source, } } @@ -258,56 +252,19 @@ func (f *CodeSampleFilter) includeCodeSamplesForOperation(pathName, opMethod str } supportedFormat := getSupportedFormat(op) - if isUnauthenticatedOperation(op) { - codeSamples = append(codeSamples, f.newUnauthenticatedCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat)) + unAuthEndpoint := isEndpointUnAuthenticated(op.Responses.Map()) + if unAuthEndpoint { + } else { - if usesServiceAccountAuth(op) { - codeSamples = append(codeSamples, f.newServiceAccountCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat)) - } - if usesDigestAuth(op) { - codeSamples = append(codeSamples, f.newDigestCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat)) - } + codeSamples = append( + codeSamples, + f.newServiceAccountCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat), + f.newDigestCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat)) } op.Extensions[codeSampleExtensionName] = codeSamples return nil } -// isUnauthenticatedOperation reports whether the operation does not require authentication. An -// operation is unauthenticated when it overrides the global security requirements with an empty set -// (i.e. "security: []"). When the operation does not override the global security (Security is nil), -// it inherits the authenticated global default. -func isUnauthenticatedOperation(op *openapi3.Operation) bool { - return op.Security != nil && len(*op.Security) == 0 -} - -// usesServiceAccountAuth reports whether the operation supports service account authentication. -// When the operation does not override the global security (Security is nil), it inherits the -// authenticated global default, which supports service accounts. -func usesServiceAccountAuth(op *openapi3.Operation) bool { - return operationSupportsSecurityScheme(op, serviceAccountSchemeName) -} - -// usesDigestAuth reports whether the operation supports digest authentication. When the operation -// does not override the global security (Security is nil), it inherits the authenticated global -// default, which supports digest authentication. -func usesDigestAuth(op *openapi3.Operation) bool { - return operationSupportsSecurityScheme(op, digestAuthSchemeName) -} - -func operationSupportsSecurityScheme(op *openapi3.Operation, scheme string) bool { - if op.Security == nil { - return true - } - - for _, requirement := range *op.Security { - if _, ok := requirement[scheme]; ok { - return true - } - } - - return false -} - // getSupportedFormat inspects the response content types of a given OpenAPI operation, // looking for a content type string in the format "application/vnd.atlas.+". // It splits the content type on the '+' character and returns the last part, which represents the supported format (e.g., "json"). @@ -351,3 +308,18 @@ func successResponseExtensions(responsesMap map[string]*openapi3.ResponseRef) op return nil } + +// isEndpointUnAuthenticated returns true if the endpoint is authenticated. +// The authentication decision is made based on the responses code the endpoint support: +// - No 401 (unauthorized) and 403(forbidden) -> unauthenticated +func isEndpointUnAuthenticated(responsesMap map[string]*openapi3.ResponseRef) bool { + if _, ok := responsesMap["401"]; ok { + return false + } + + if _, ok := responsesMap["403"]; ok { + return false + } + + return true +} diff --git a/tools/foas/openapi/filter/code_sample_test.go b/tools/foas/openapi/filter/code_sample_test.go index 1d1ba9db15..f1a9b6f952 100644 --- a/tools/foas/openapi/filter/code_sample_test.go +++ b/tools/foas/openapi/filter/code_sample_test.go @@ -629,207 +629,6 @@ func TestCodeSampleFilter(t *testing.T) { })), }, }, - { - name: "unauthenticated api omits authentication from the curl sample", - version: "preview", - oas: &openapi3.T{ - Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ - Get: &openapi3.Operation{ - OperationID: "testOperationID", - Summary: "testSummary", - Tags: []string{"TestTag"}, - Security: openapi3.NewSecurityRequirements(), - Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ - Content: openapi3.Content{ - "application/vnd.atlas.preview+json": { - Schema: &openapi3.SchemaRef{ - Ref: "#/components/schemas/PaginatedAppUserView", - }, - Extensions: map[string]any{ - "x-gen-version": "preview", - }, - }, - }, - })), - Extensions: map[string]any{ - "x-sunset": "9999-12-31", - }, - }, - })), - }, - expectedOas: &openapi3.T{ - Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ - Get: &openapi3.Operation{ - OperationID: "testOperationID", - Summary: "testSummary", - Tags: []string{"TestTag"}, - Security: openapi3.NewSecurityRequirements(), - Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ - Content: openapi3.Content{ - "application/vnd.atlas.preview+json": { - Schema: &openapi3.SchemaRef{ - Ref: "#/components/schemas/PaginatedAppUserView", - }, - Extensions: map[string]any{ - "x-gen-version": "preview", - }, - }, - }, - })), - Extensions: map[string]any{ - "x-sunset": "9999-12-31", - "x-codeSamples": []codeSample{ - { - Lang: "cURL", - Label: "Atlas CLI", - Source: "atlas api testTag testOperationId --help", - }, - { - Lang: "cURL", - Label: "curl", - Source: "curl --include \\\n " + - "--header \"Accept: application/vnd.atlas.preview+json\" \\\n " + "-X GET \"https://cloud.mongodb.com/test?pretty=true\"", - }, - }, - }, - }, - })), - }, - }, - { - name: "service account only api omits the digest curl sample", - version: "preview", - oas: &openapi3.T{ - Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ - Get: &openapi3.Operation{ - OperationID: "testOperationID", - Summary: "testSummary", - Tags: []string{"TestTag"}, - Security: openapi3.NewSecurityRequirements().With(openapi3.NewSecurityRequirement().Authenticate("ServiceAccounts")), - Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ - Content: openapi3.Content{ - "application/vnd.atlas.preview+json": { - Schema: &openapi3.SchemaRef{ - Ref: "#/components/schemas/PaginatedAppUserView", - }, - Extensions: map[string]any{ - "x-gen-version": "preview", - }, - }, - }, - })), - Extensions: map[string]any{ - "x-sunset": "9999-12-31", - }, - }, - })), - }, - expectedOas: &openapi3.T{ - Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ - Get: &openapi3.Operation{ - OperationID: "testOperationID", - Summary: "testSummary", - Tags: []string{"TestTag"}, - Security: openapi3.NewSecurityRequirements().With(openapi3.NewSecurityRequirement().Authenticate("ServiceAccounts")), - Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ - Content: openapi3.Content{ - "application/vnd.atlas.preview+json": { - Schema: &openapi3.SchemaRef{ - Ref: "#/components/schemas/PaginatedAppUserView", - }, - Extensions: map[string]any{ - "x-gen-version": "preview", - }, - }, - }, - })), - Extensions: map[string]any{ - "x-sunset": "9999-12-31", - "x-codeSamples": []codeSample{ - { - Lang: "cURL", - Label: "Atlas CLI", - Source: "atlas api testTag testOperationId --help", - }, - { - Lang: "cURL", - Label: "curl (Service Accounts)", - Source: "curl --include --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n " + - "--header \"Accept: application/vnd.atlas.preview+json\" \\\n " + "-X GET \"https://cloud.mongodb.com/test?pretty=true\"", - }, - }, - }, - }, - })), - }, - }, - { - name: "digest only api omits the service account curl sample", - version: "preview", - oas: &openapi3.T{ - Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ - Get: &openapi3.Operation{ - OperationID: "testOperationID", - Summary: "testSummary", - Tags: []string{"TestTag"}, - Security: openapi3.NewSecurityRequirements().With(openapi3.NewSecurityRequirement().Authenticate("DigestAuth")), - Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ - Content: openapi3.Content{ - "application/vnd.atlas.preview+json": { - Schema: &openapi3.SchemaRef{ - Ref: "#/components/schemas/PaginatedAppUserView", - }, - Extensions: map[string]any{ - "x-gen-version": "preview", - }, - }, - }, - })), - Extensions: map[string]any{ - "x-sunset": "9999-12-31", - }, - }, - })), - }, - expectedOas: &openapi3.T{ - Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ - Get: &openapi3.Operation{ - OperationID: "testOperationID", - Summary: "testSummary", - Tags: []string{"TestTag"}, - Security: openapi3.NewSecurityRequirements().With(openapi3.NewSecurityRequirement().Authenticate("DigestAuth")), - Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ - Content: openapi3.Content{ - "application/vnd.atlas.preview+json": { - Schema: &openapi3.SchemaRef{ - Ref: "#/components/schemas/PaginatedAppUserView", - }, - Extensions: map[string]any{ - "x-gen-version": "preview", - }, - }, - }, - })), - Extensions: map[string]any{ - "x-sunset": "9999-12-31", - "x-codeSamples": []codeSample{ - { - Lang: "cURL", - Label: "Atlas CLI", - Source: "atlas api testTag testOperationId --help", - }, - { - Lang: "cURL", - Label: "curl (Digest)", - Source: "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest --include \\\n " + - "--header \"Accept: application/vnd.atlas.preview+json\" \\\n " + "-X GET \"https://cloud.mongodb.com/test?pretty=true\"", - }, - }, - }, - }, - })), - }, - }, } for _, tt := range testCases { From 49682cf761068a9aa712a57149c21d5749de4234 Mon Sep 17 00:00:00 2001 From: Andrea Angiolillo Date: Tue, 28 Jul 2026 12:53:12 +0100 Subject: [PATCH 3/7] Update code_sample.go --- tools/foas/openapi/filter/code_sample.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/foas/openapi/filter/code_sample.go b/tools/foas/openapi/filter/code_sample.go index 2f79ddedf8..d4c6ef22a3 100644 --- a/tools/foas/openapi/filter/code_sample.go +++ b/tools/foas/openapi/filter/code_sample.go @@ -254,7 +254,7 @@ func (f *CodeSampleFilter) includeCodeSamplesForOperation(pathName, opMethod str supportedFormat := getSupportedFormat(op) unAuthEndpoint := isEndpointUnAuthenticated(op.Responses.Map()) if unAuthEndpoint { - + codeSamples = append(codeSamples, f.newCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat)) } else { codeSamples = append( codeSamples, From db2040b37ead7f7b6a9f4af40f8ea837405fcd53 Mon Sep 17 00:00:00 2001 From: Andrea Angiolillo Date: Tue, 28 Jul 2026 13:07:26 +0100 Subject: [PATCH 4/7] Update code_sample_test.go --- tools/foas/openapi/filter/code_sample_test.go | 265 +++++++++++++++++- 1 file changed, 251 insertions(+), 14 deletions(-) diff --git a/tools/foas/openapi/filter/code_sample_test.go b/tools/foas/openapi/filter/code_sample_test.go index f1a9b6f952..148105704a 100644 --- a/tools/foas/openapi/filter/code_sample_test.go +++ b/tools/foas/openapi/filter/code_sample_test.go @@ -50,7 +50,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - })), + }), openapi3.WithName("401", &openapi3.Response{})), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -74,7 +74,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - })), + }), openapi3.WithName("401", &openapi3.Response{})), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -140,7 +140,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - })), + }), openapi3.WithName("401", &openapi3.Response{})), Extensions: map[string]any{ "x-sunset": "9999-12-31", }, @@ -164,7 +164,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - })), + }), openapi3.WithName("401", &openapi3.Response{})), Extensions: map[string]any{ "x-sunset": "9999-12-31", "x-codeSamples": []codeSample{ @@ -211,7 +211,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - })), + }), openapi3.WithName("401", &openapi3.Response{})), Extensions: map[string]any{ "x-sunset": "9999-12-31", }, @@ -235,7 +235,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - })), + }), openapi3.WithName("401", &openapi3.Response{})), Extensions: map[string]any{ "x-sunset": "9999-12-31", "x-codeSamples": []codeSample{ @@ -281,7 +281,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - })), + }), openapi3.WithName("401", &openapi3.Response{})), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -305,7 +305,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - })), + }), openapi3.WithName("401", &openapi3.Response{})), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -372,7 +372,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - })), + }), openapi3.WithName("401", &openapi3.Response{})), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -397,7 +397,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - })), + }), openapi3.WithName("401", &openapi3.Response{})), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -463,7 +463,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - })), + }), openapi3.WithName("401", &openapi3.Response{})), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -490,7 +490,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - })), + }), openapi3.WithName("401", &openapi3.Response{})), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -558,7 +558,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - })), + }), openapi3.WithName("401", &openapi3.Response{})), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -585,7 +585,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - })), + }), openapi3.WithName("401", &openapi3.Response{})), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -629,6 +629,243 @@ func TestCodeSampleFilter(t *testing.T) { })), }, }, + { + name: "unauthenticated stable api emits plain curl code sample", + version: "2025-01-01", + oas: &openapi3.T{ + Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ + Get: &openapi3.Operation{ + OperationID: "testOperationID", + Summary: "testSummary", + Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ + Content: openapi3.Content{ + "application/vnd.atlas.2025-01-01+json": { + Schema: &openapi3.SchemaRef{ + Ref: "#/components/schemas/PaginatedAppUserView", + }, + Extensions: map[string]any{ + "x-gen-version": "2025-01-01", + }, + }, + }, + })), + Tags: []string{"TestTag"}, + Extensions: map[string]any{ + "x-sunset": "9999-12-31", + }, + }, + })), + }, + expectedOas: &openapi3.T{ + Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ + Get: &openapi3.Operation{ + OperationID: "testOperationID", + Summary: "testSummary", + Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ + Content: openapi3.Content{ + "application/vnd.atlas.2025-01-01+json": { + Schema: &openapi3.SchemaRef{ + Ref: "#/components/schemas/PaginatedAppUserView", + }, + Extensions: map[string]any{ + "x-gen-version": "2025-01-01", + }, + }, + }, + })), + Tags: []string{"TestTag"}, + Extensions: map[string]any{ + "x-sunset": "9999-12-31", + "x-codeSamples": []codeSample{ + { + Lang: "cURL", + Label: "Atlas CLI", + Source: "atlas api testTag testOperationId --help", + }, + { + Lang: "go", + Label: "Go", + Source: "import (\n" + + "\t\"os\"\n \"context\"\n" + "\t\"log\"\n" + + "\tsdk \"go.mongodb.org/atlas-sdk/v20250101001/admin\"\n)\n\n" + + "func main() {\n" + + "\tctx := context.Background()\n" + + "\tclientID := os.Getenv(\"MONGODB_ATLAS_CLIENT_ID\")\n" + + "\tclientSecret := os.Getenv(\"MONGODB_ATLAS_CLIENT_SECRET\")\n\n" + + "\t// See https://dochub.mongodb.org/core/atlas-go-sdk-oauth\n" + + "\tclient, err := sdk.NewClient(sdk.UseOAuthAuth(clientID, clientSecret))\n\n" + + "\tif err != nil {\n" + "\t\tlog.Fatalf(\"Error: %v\", err)\n\t}\n\n" + + "\tparams = &sdk.TestOperationIDApiParams{}\n" + + "\tsdkResp, httpResp, err := client.TestTagApi.\n" + + "\t\tTestOperationIDWithParams(ctx, params).\n" + + "\t\tExecute()" + "\n}\n", + }, + { + Lang: "cURL", + Label: "curl", + Source: "curl --include \\\n " + + "--header \"Accept: application/vnd.atlas.2025-01-01+json\" \\\n " + "-X GET \"https://cloud.mongodb.com/test?pretty=true\"", + }, + }, + }, + }, + })), + }, + }, + { + name: "unauthenticated preview api emits plain curl code sample", + version: "preview", + oas: &openapi3.T{ + Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ + Get: &openapi3.Operation{ + OperationID: "testOperationID", + Summary: "testSummary", + Tags: []string{"TestTag"}, + Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ + Content: openapi3.Content{ + "application/vnd.atlas.preview+json": { + Schema: &openapi3.SchemaRef{ + Ref: "#/components/schemas/PaginatedAppUserView", + }, + Extensions: map[string]any{ + "x-gen-version": "preview", + }, + }, + }, + })), + Extensions: map[string]any{ + "x-sunset": "9999-12-31", + }, + }, + })), + }, + expectedOas: &openapi3.T{ + Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ + Get: &openapi3.Operation{ + OperationID: "testOperationID", + Summary: "testSummary", + Tags: []string{"TestTag"}, + Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ + Content: openapi3.Content{ + "application/vnd.atlas.preview+json": { + Schema: &openapi3.SchemaRef{ + Ref: "#/components/schemas/PaginatedAppUserView", + }, + Extensions: map[string]any{ + "x-gen-version": "preview", + }, + }, + }, + })), + Extensions: map[string]any{ + "x-sunset": "9999-12-31", + "x-codeSamples": []codeSample{ + { + Lang: "cURL", + Label: "Atlas CLI", + Source: "atlas api testTag testOperationId --help", + }, + { + Lang: "cURL", + Label: "curl", + Source: "curl --include \\\n " + + "--header \"Accept: application/vnd.atlas.preview+json\" \\\n " + "-X GET \"https://cloud.mongodb.com/test?pretty=true\"", + }, + }, + }, + }, + })), + }, + }, + { + name: "authenticated api with 403 response emits service accounts and digest curl code samples", + version: "2025-01-01", + oas: &openapi3.T{ + Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ + Get: &openapi3.Operation{ + OperationID: "testOperationID", + Summary: "testSummary", + Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ + Content: openapi3.Content{ + "application/vnd.atlas.2025-01-01+json": { + Schema: &openapi3.SchemaRef{ + Ref: "#/components/schemas/PaginatedAppUserView", + }, + Extensions: map[string]any{ + "x-gen-version": "2025-01-01", + }, + }, + }, + }), openapi3.WithName("403", &openapi3.Response{})), + Tags: []string{"TestTag"}, + Extensions: map[string]any{ + "x-sunset": "9999-12-31", + }, + }, + })), + }, + expectedOas: &openapi3.T{ + Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ + Get: &openapi3.Operation{ + OperationID: "testOperationID", + Summary: "testSummary", + Responses: openapi3.NewResponses(openapi3.WithName("200", &openapi3.Response{ + Content: openapi3.Content{ + "application/vnd.atlas.2025-01-01+json": { + Schema: &openapi3.SchemaRef{ + Ref: "#/components/schemas/PaginatedAppUserView", + }, + Extensions: map[string]any{ + "x-gen-version": "2025-01-01", + }, + }, + }, + }), openapi3.WithName("403", &openapi3.Response{})), + Tags: []string{"TestTag"}, + Extensions: map[string]any{ + "x-sunset": "9999-12-31", + "x-codeSamples": []codeSample{ + { + Lang: "cURL", + Label: "Atlas CLI", + Source: "atlas api testTag testOperationId --help", + }, + { + Lang: "go", + Label: "Go", + Source: "import (\n" + + "\t\"os\"\n \"context\"\n" + "\t\"log\"\n" + + "\tsdk \"go.mongodb.org/atlas-sdk/v20250101001/admin\"\n)\n\n" + + "func main() {\n" + + "\tctx := context.Background()\n" + + "\tclientID := os.Getenv(\"MONGODB_ATLAS_CLIENT_ID\")\n" + + "\tclientSecret := os.Getenv(\"MONGODB_ATLAS_CLIENT_SECRET\")\n\n" + + "\t// See https://dochub.mongodb.org/core/atlas-go-sdk-oauth\n" + + "\tclient, err := sdk.NewClient(sdk.UseOAuthAuth(clientID, clientSecret))\n\n" + + "\tif err != nil {\n" + "\t\tlog.Fatalf(\"Error: %v\", err)\n\t}\n\n" + + "\tparams = &sdk.TestOperationIDApiParams{}\n" + + "\tsdkResp, httpResp, err := client.TestTagApi.\n" + + "\t\tTestOperationIDWithParams(ctx, params).\n" + + "\t\tExecute()" + "\n}\n", + }, + { + Lang: "cURL", + Label: "curl (Service Accounts)", + Source: "curl --include --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n " + + "--header \"Accept: application/vnd.atlas.2025-01-01+json\" \\\n " + "-X GET \"https://cloud.mongodb.com/test?pretty=true\"", + }, + { + Lang: "cURL", + Label: "curl (Digest)", + Source: "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest --include \\\n " + + "--header \"Accept: application/vnd.atlas.2025-01-01+json\" \\\n " + "-X GET \"https://cloud.mongodb.com/test?pretty=true\"", + }, + }, + }, + }, + })), + }, + }, } for _, tt := range testCases { From c255d266d97d060b9819cd05f1b8d719b0ca2268 Mon Sep 17 00:00:00 2001 From: Andrea Angiolillo Date: Tue, 28 Jul 2026 13:11:26 +0100 Subject: [PATCH 5/7] Update code_sample.go --- tools/foas/openapi/filter/code_sample.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/foas/openapi/filter/code_sample.go b/tools/foas/openapi/filter/code_sample.go index d4c6ef22a3..2bf39757c1 100644 --- a/tools/foas/openapi/filter/code_sample.go +++ b/tools/foas/openapi/filter/code_sample.go @@ -311,7 +311,7 @@ func successResponseExtensions(responsesMap map[string]*openapi3.ResponseRef) op // isEndpointUnAuthenticated returns true if the endpoint is authenticated. // The authentication decision is made based on the responses code the endpoint support: -// - No 401 (unauthorized) and 403(forbidden) -> unauthenticated +// - No 401 (unauthorized) and 403(forbidden) -> unauthenticated. func isEndpointUnAuthenticated(responsesMap map[string]*openapi3.ResponseRef) bool { if _, ok := responsesMap["401"]; ok { return false From 79213a9b280e95a3470b43966972463094804032 Mon Sep 17 00:00:00 2001 From: Andrea Angiolillo Date: Tue, 28 Jul 2026 13:53:36 +0100 Subject: [PATCH 6/7] Addressed Lovisa's comment --- tools/foas/openapi/filter/code_sample.go | 19 +++------- tools/foas/openapi/filter/code_sample_test.go | 38 ++++++++++--------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/tools/foas/openapi/filter/code_sample.go b/tools/foas/openapi/filter/code_sample.go index 2bf39757c1..5818314254 100644 --- a/tools/foas/openapi/filter/code_sample.go +++ b/tools/foas/openapi/filter/code_sample.go @@ -252,7 +252,7 @@ func (f *CodeSampleFilter) includeCodeSamplesForOperation(pathName, opMethod str } supportedFormat := getSupportedFormat(op) - unAuthEndpoint := isEndpointUnAuthenticated(op.Responses.Map()) + unAuthEndpoint := isEndpointUnAuthenticated(op.Extensions) if unAuthEndpoint { codeSamples = append(codeSamples, f.newCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat)) } else { @@ -310,16 +310,9 @@ func successResponseExtensions(responsesMap map[string]*openapi3.ResponseRef) op } // isEndpointUnAuthenticated returns true if the endpoint is authenticated. -// The authentication decision is made based on the responses code the endpoint support: -// - No 401 (unauthorized) and 403(forbidden) -> unauthenticated. -func isEndpointUnAuthenticated(responsesMap map[string]*openapi3.ResponseRef) bool { - if _, ok := responsesMap["401"]; ok { - return false - } - - if _, ok := responsesMap["403"]; ok { - return false - } - - return true +// The authentication decision is made based on the present of the extension "security". If "security":[] is present, +// the endpoint is considered unauthenticated. +func isEndpointUnAuthenticated(extensions map[string]any) bool { + _, ok := extensions["security"] + return ok } diff --git a/tools/foas/openapi/filter/code_sample_test.go b/tools/foas/openapi/filter/code_sample_test.go index 148105704a..d39337f3ac 100644 --- a/tools/foas/openapi/filter/code_sample_test.go +++ b/tools/foas/openapi/filter/code_sample_test.go @@ -50,7 +50,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("401", &openapi3.Response{})), + })), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -74,7 +74,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("401", &openapi3.Response{})), + })), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -140,7 +140,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("401", &openapi3.Response{})), + })), Extensions: map[string]any{ "x-sunset": "9999-12-31", }, @@ -164,7 +164,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("401", &openapi3.Response{})), + })), Extensions: map[string]any{ "x-sunset": "9999-12-31", "x-codeSamples": []codeSample{ @@ -211,7 +211,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("401", &openapi3.Response{})), + })), Extensions: map[string]any{ "x-sunset": "9999-12-31", }, @@ -235,7 +235,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("401", &openapi3.Response{})), + })), Extensions: map[string]any{ "x-sunset": "9999-12-31", "x-codeSamples": []codeSample{ @@ -281,7 +281,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("401", &openapi3.Response{})), + })), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -305,7 +305,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("401", &openapi3.Response{})), + })), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -372,7 +372,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("401", &openapi3.Response{})), + })), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -397,7 +397,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("401", &openapi3.Response{})), + })), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -463,7 +463,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("401", &openapi3.Response{})), + })), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -490,7 +490,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("401", &openapi3.Response{})), + })), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -558,7 +558,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("401", &openapi3.Response{})), + })), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -585,7 +585,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("401", &openapi3.Response{})), + })), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -652,6 +652,7 @@ func TestCodeSampleFilter(t *testing.T) { Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", + "security": []any{}, }, }, })), @@ -676,6 +677,7 @@ func TestCodeSampleFilter(t *testing.T) { Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", + "security": []any{}, "x-codeSamples": []codeSample{ { Lang: "cURL", @@ -735,6 +737,7 @@ func TestCodeSampleFilter(t *testing.T) { })), Extensions: map[string]any{ "x-sunset": "9999-12-31", + "security": []any{}, }, }, })), @@ -759,6 +762,7 @@ func TestCodeSampleFilter(t *testing.T) { })), Extensions: map[string]any{ "x-sunset": "9999-12-31", + "security": []any{}, "x-codeSamples": []codeSample{ { Lang: "cURL", @@ -778,7 +782,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, { - name: "authenticated api with 403 response emits service accounts and digest curl code samples", + name: "authenticated api without security extension emits service accounts and digest curl code samples", version: "2025-01-01", oas: &openapi3.T{ Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{ @@ -796,7 +800,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("403", &openapi3.Response{})), + })), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", @@ -820,7 +824,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, }, - }), openapi3.WithName("403", &openapi3.Response{})), + })), Tags: []string{"TestTag"}, Extensions: map[string]any{ "x-sunset": "9999-12-31", From f87d859ab4432405b9579f82ae5adea865245870 Mon Sep 17 00:00:00 2001 From: Andrea Angiolillo Date: Tue, 28 Jul 2026 13:55:58 +0100 Subject: [PATCH 7/7] fix --- tools/foas/openapi/filter/code_sample.go | 14 +++++++------- tools/foas/openapi/filter/code_sample_test.go | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tools/foas/openapi/filter/code_sample.go b/tools/foas/openapi/filter/code_sample.go index 5818314254..d296b6f942 100644 --- a/tools/foas/openapi/filter/code_sample.go +++ b/tools/foas/openapi/filter/code_sample.go @@ -252,7 +252,7 @@ func (f *CodeSampleFilter) includeCodeSamplesForOperation(pathName, opMethod str } supportedFormat := getSupportedFormat(op) - unAuthEndpoint := isEndpointUnAuthenticated(op.Extensions) + unAuthEndpoint := isEndpointUnAuthenticated(op) if unAuthEndpoint { codeSamples = append(codeSamples, f.newCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat)) } else { @@ -309,10 +309,10 @@ func successResponseExtensions(responsesMap map[string]*openapi3.ResponseRef) op return nil } -// isEndpointUnAuthenticated returns true if the endpoint is authenticated. -// The authentication decision is made based on the present of the extension "security". If "security":[] is present, -// the endpoint is considered unauthenticated. -func isEndpointUnAuthenticated(extensions map[string]any) bool { - _, ok := extensions["security"] - return ok +// isEndpointUnAuthenticated returns true if the endpoint is unauthenticated. +// The authentication decision is made based on the operation-level "security" field: +// when it is present and empty ("security": []), the endpoint does not require authentication. +// Note: kin-openapi parses "security" into Operation.Security, not into Operation.Extensions. +func isEndpointUnAuthenticated(op *openapi3.Operation) bool { + return op.Security != nil && len(*op.Security) == 0 } diff --git a/tools/foas/openapi/filter/code_sample_test.go b/tools/foas/openapi/filter/code_sample_test.go index d39337f3ac..2d1bb78757 100644 --- a/tools/foas/openapi/filter/code_sample_test.go +++ b/tools/foas/openapi/filter/code_sample_test.go @@ -649,10 +649,10 @@ func TestCodeSampleFilter(t *testing.T) { }, }, })), - Tags: []string{"TestTag"}, + Tags: []string{"TestTag"}, + Security: &openapi3.SecurityRequirements{}, Extensions: map[string]any{ "x-sunset": "9999-12-31", - "security": []any{}, }, }, })), @@ -674,10 +674,10 @@ func TestCodeSampleFilter(t *testing.T) { }, }, })), - Tags: []string{"TestTag"}, + Tags: []string{"TestTag"}, + Security: &openapi3.SecurityRequirements{}, Extensions: map[string]any{ "x-sunset": "9999-12-31", - "security": []any{}, "x-codeSamples": []codeSample{ { Lang: "cURL", @@ -735,9 +735,9 @@ func TestCodeSampleFilter(t *testing.T) { }, }, })), + Security: &openapi3.SecurityRequirements{}, Extensions: map[string]any{ "x-sunset": "9999-12-31", - "security": []any{}, }, }, })), @@ -760,9 +760,9 @@ func TestCodeSampleFilter(t *testing.T) { }, }, })), + Security: &openapi3.SecurityRequirements{}, Extensions: map[string]any{ "x-sunset": "9999-12-31", - "security": []any{}, "x-codeSamples": []codeSample{ { Lang: "cURL", @@ -782,7 +782,7 @@ func TestCodeSampleFilter(t *testing.T) { }, }, { - name: "authenticated api without security extension emits service accounts and digest curl code samples", + name: "authenticated api without empty security field emits service accounts and digest curl code samples", version: "2025-01-01", oas: &openapi3.T{ Paths: openapi3.NewPaths(openapi3.WithPath("/test", &openapi3.PathItem{