diff --git a/tools/foas/openapi/filter/code_sample.go b/tools/foas/openapi/filter/code_sample.go index cac09a80b6..0fc824c326 100644 --- a/tools/foas/openapi/filter/code_sample.go +++ b/tools/foas/openapi/filter/code_sample.go @@ -34,6 +34,7 @@ import ( var goSDKTemplate string const codeSampleExtensionName = "x-codeSamples" +const atlasCliExtensionName = "x-xgen-atlascli" // https://redocly.com/docs-legacy/api-reference-docs/specification-extensions/x-code-samples#x-codesamples type codeSample struct { @@ -147,6 +148,19 @@ func apiVersion(version *apiversion.APIVersion) string { return version.Date().Format(time.DateOnly) + ".upcoming" } +// skipAtlasCliCodeSample reports whether the Atlas CLI code sample generation should be skipped +// for the given operation. It is skipped only when the "x-xgen-atlascli" extension is present and +// sets "skip" to true, since in that case atlascli owns the command generation. +func skipAtlasCliCodeSample(op *openapi3.Operation) bool { + atlasCli, ok := op.Extensions[atlasCliExtensionName].(map[string]any) + if !ok { + return false + } + + skip, _ := atlasCli["skip"].(bool) + return skip +} + func newAtlasCliCodeSamplesForOperation(op *openapi3.Operation) codeSample { tag := strcase.ToLowerCamel(op.Tags[0]) operationID := strcase.ToLowerCamel(op.OperationID) @@ -215,8 +229,12 @@ func (f *CodeSampleFilter) includeCodeSamplesForOperation(pathName, opMethod str op.Extensions = map[string]any{} } - codeSamples := []codeSample{ - newAtlasCliCodeSamplesForOperation(op), + // The Atlas CLI code sample is generated when the "x-xgen-atlascli" extension is missing + // or when it is present with "skip: false". When the extension sets "skip: true", atlascli + // owns the command generation and no Atlas CLI sample is emitted. + var codeSamples []codeSample + if !skipAtlasCliCodeSample(op) { + codeSamples = append(codeSamples, newAtlasCliCodeSamplesForOperation(op)) } if f.metadata.targetVersion.IsStable() { diff --git a/tools/foas/openapi/filter/code_sample_test.go b/tools/foas/openapi/filter/code_sample_test.go index d0c09ebe53..f1a9b6f952 100644 --- a/tools/foas/openapi/filter/code_sample_test.go +++ b/tools/foas/openapi/filter/code_sample_test.go @@ -444,6 +444,191 @@ func TestCodeSampleFilter(t *testing.T) { })), }, }, + { + name: "stable api with x-xgen-atlascli skip false emits the Atlas CLI 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", + "x-xgen-atlascli": map[string]any{ + "skip": false, + }, + }, + }, + })), + }, + 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-xgen-atlascli": map[string]any{ + "skip": false, + }, + "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\"", + }, + }, + }, + }, + })), + }, + }, + { + name: "stable api with x-xgen-atlascli skips the Atlas CLI 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", + "x-xgen-atlascli": map[string]any{ + "skip": true, + }, + }, + }, + })), + }, + 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-xgen-atlascli": map[string]any{ + "skip": true, + }, + "x-codeSamples": []codeSample{ + { + 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 {