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
71 changes: 45 additions & 26 deletions tools/foas/openapi/filter/code_sample.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,9 @@ 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 "

// 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
Expand All @@ -90,7 +88,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":
Expand All @@ -99,34 +96,43 @@ func (f *CodeSampleFilter) newDigestCurlCodeSamplesForOperation(pathName, opMeth
source += "-d " + "'{ <Payload> }'"
}

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 "

source = appendCurlMethodSource(source, pathName, opMethod, format)

return codeSample{
Lang: "cURL",
Label: "curl (Digest)",
Source: source,
}
}

func (f *CodeSampleFilter) newCurlCodeSamplesForOperation(pathName, opMethod, format string) codeSample {
version := apiVersion(f.metadata.targetVersion)
source := "curl --include \\\n " +
"--header \"Accept: application/vnd.atlas." + version + "+" + format + "\" \\\n "

source = appendCurlMethodSource(source, pathName, opMethod, format)

return codeSample{
Lang: "cURL",
Label: "curl",
Source: source,
}
}

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 "

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 " + "'{ <Payload> }'"
}
source = appendCurlMethodSource(source, pathName, opMethod, format)

return codeSample{
Lang: "cURL",
Expand Down Expand Up @@ -246,10 +252,15 @@ func (f *CodeSampleFilter) includeCodeSamplesForOperation(pathName, opMethod str
}

supportedFormat := getSupportedFormat(op)
codeSamples = append(
codeSamples,
f.newServiceAccountCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat),
f.newDigestCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat))
unAuthEndpoint := isEndpointUnAuthenticated(op)
if unAuthEndpoint {
codeSamples = append(codeSamples, f.newCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat))
} else {
codeSamples = append(
codeSamples,
f.newServiceAccountCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat),
f.newDigestCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat))
}
op.Extensions[codeSampleExtensionName] = codeSamples
return nil
}
Expand Down Expand Up @@ -297,3 +308,11 @@ func successResponseExtensions(responsesMap map[string]*openapi3.ResponseRef) op

return nil
}

// 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
}
241 changes: 241 additions & 0 deletions tools/foas/openapi/filter/code_sample_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,247 @@ 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"},
Security: &openapi3.SecurityRequirements{},
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"},
Security: &openapi3.SecurityRequirements{},
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",
},
},
},
})),
Security: &openapi3.SecurityRequirements{},
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",
},
},
},
})),
Security: &openapi3.SecurityRequirements{},
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 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{
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 (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 {
Expand Down
Loading