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
2 changes: 1 addition & 1 deletion .github/workflows/code-health-foascli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,4 @@ jobs:
go-version-file: 'tools/cli/go.mod'
- name: Run e2e tests
working-directory: tools/cli
run: make e2e-test
run: make devtools e2e-test
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/tools/cli/test/e2e/cli/output/**
!/tools/cli/test/e2e/cli/output/.gitkeep
/tools/*/dist/*
/tools/*/output/*
# We don't want to commit env variables
*.env

Expand Down
2 changes: 2 additions & 0 deletions tools/cli/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ deps: ## Download go module dependencies
devtools: ## Install dev tools
@echo "==> Installing dev tools..."
go install go.uber.org/mock/mockgen@latest
go install github.com/tufin/[email protected]
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin $(GOLANGCI_VERSION)

.PHONY: setup
Expand Down Expand Up @@ -70,6 +71,7 @@ list: ## List all make targets
.PHONY: e2e-test
e2e-test: build ## Run E2E tests
@echo "==> Running E2E tests..."
rm -rf test/e2e/cli/output
$(TEST_CMD) -v -p 1 -parallel $(E2E_PARALLEL) -timeout $(E2E_TIMEOUT) ./test/e2e... $(E2E_EXTRA_ARGS)

.PHONY: gen-docs
Expand Down
83 changes: 75 additions & 8 deletions tools/cli/internal/openapi/filter/code_sample.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,30 @@ func (f *CodeSampleFilter) Apply() error {
return nil
}

func (f *CodeSampleFilter) newDigestCurlCodeSamplesForOperation(pathName, opMethod string) codeSample {
func getFileExtension(format string) string {
switch format {
case "gzip":
return "gz"
default:
return format
}
}

func (f *CodeSampleFilter) newDigestCurlCodeSamplesForOperation(pathName, opMethod, format string) codeSample {
version := apiVersion(f.metadata.targetVersion)
source := "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest \\\n " +
"--header \"Accept: application/vnd.atlas." + version + "+json\" \\\n "
"--header \"Accept: application/vnd.atlas." + version + "+" + format + "\" \\\n "

switch opMethod {
case "GET":
source += "-X " + opMethod + " \"https://cloud.mongodb.com" + pathName + "?pretty=true\""
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":
Expand All @@ -88,14 +104,20 @@ func (f *CodeSampleFilter) newDigestCurlCodeSamplesForOperation(pathName, opMeth
}
}

func (f *CodeSampleFilter) newServiceAccountCurlCodeSamplesForOperation(pathName, opMethod string) codeSample {
func (f *CodeSampleFilter) newServiceAccountCurlCodeSamplesForOperation(pathName, opMethod, format string) codeSample {
version := apiVersion(f.metadata.targetVersion)
source := "curl --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n " +
"--header \"Accept: application/vnd.atlas." + version + "+json\" \\\n "
"--header \"Accept: application/vnd.atlas." + version + "+" + format + "\" \\\n "

switch opMethod {
case "GET":
source += "-X " + opMethod + " \"https://cloud.mongodb.com" + pathName + "?pretty=true\""
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":
Expand Down Expand Up @@ -193,10 +215,55 @@ func (f *CodeSampleFilter) includeCodeSamplesForOperation(pathName, opMethod str
codeSamples = append(codeSamples, *sdkSample)
}

supportedFormat := getSupportedFormat(op)
codeSamples = append(
codeSamples,
f.newServiceAccountCurlCodeSamplesForOperation(pathName, opMethod),
f.newDigestCurlCodeSamplesForOperation(pathName, opMethod))
f.newServiceAccountCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat),
f.newDigestCurlCodeSamplesForOperation(pathName, opMethod, supportedFormat))
op.Extensions[codeSampleExtensionName] = codeSamples
return nil
}

// getSupportedFormat inspects the response content types of a given OpenAPI operation,
// looking for a content type string in the format "application/vnd.atlas.<api_version>+<supported_format>".
// It splits the content type on the '+' character and returns the last part, which represents the supported format (e.g., "json").
// If no such content type is found, it defaults to returning "json".
func getSupportedFormat(op *openapi3.Operation) string {
responseMap := successResponseExtensions(op.Responses.Map())
format := "json"
for k := range responseMap {
// k is a string with the format "application/vnd.atlas.<api_version>+<supported_format>"
parts := strings.Split(k, "+")
if len(parts) == 0 {
continue
}

format = parts[len(parts)-1]
// If the endpoint supports "json", we return it as "json" is the best supported format in our APIs
// and users should use it when available.
if format == "json" {
return format
}
}

return format
}

// successResponseExtensions returns the Content object of the first successful HTTP response (status 200, 201, 202, or 204)
// found in the provided responses map.
func successResponseExtensions(responsesMap map[string]*openapi3.ResponseRef) openapi3.Content {
if val, ok := responsesMap["200"]; ok {
return val.Value.Content
}
if val, ok := responsesMap["201"]; ok {
return val.Value.Content
}
if val, ok := responsesMap["202"]; ok {
return val.Value.Content
}
if val, ok := responsesMap["204"]; ok {
return val.Value.Content
}

return nil
}
91 changes: 91 additions & 0 deletions tools/cli/internal/openapi/filter/code_sample_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,97 @@ func TestCodeSampleFilter(t *testing.T) {
})),
},
},
{
name: "stable api gzip",
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+gzip": {
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+gzip": {
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 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 --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n " +
"--header \"Accept: application/vnd.atlas.2025-01-01+gzip\" \\\n " +
"-X GET \"https://cloud.mongodb.com/test\" \\\n --output \"file_name.gz\"",
},
{
Lang: "cURL",
Label: "curl (Digest)",
Source: "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest \\\n " +
"--header \"Accept: application/vnd.atlas.2025-01-01+gzip\" \\\n " +
"-X GET \"https://cloud.mongodb.com/test\" \\\n --output \"file_name.gz\"",
},
},
},
},
})),
},
},
}

for _, tt := range testCases {
Expand Down
16 changes: 8 additions & 8 deletions tools/cli/test/data/split/dev/openapi-v2-2023-01-01.json
Original file line number Diff line number Diff line change
Expand Up @@ -43904,12 +43904,12 @@
{
"lang": "cURL",
"label": "curl (Service Accounts)",
"source": "curl --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n --header \"Accept: application/vnd.atlas.2023-01-01+json\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/onlineArchives/queryLogs.gz?pretty=true\""
"source": "curl --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n --header \"Accept: application/vnd.atlas.2023-01-01+gzip\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/onlineArchives/queryLogs.gz\" \\\n --output \"file_name.gz\""
},
{
"lang": "cURL",
"label": "curl (Digest)",
"source": "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest \\\n --header \"Accept: application/vnd.atlas.2023-01-01+json\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/onlineArchives/queryLogs.gz?pretty=true\""
"source": "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest \\\n --header \"Accept: application/vnd.atlas.2023-01-01+gzip\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/onlineArchives/queryLogs.gz\" \\\n --output \"file_name.gz\""
}
]
}
Expand Down Expand Up @@ -46165,12 +46165,12 @@
{
"lang": "cURL",
"label": "curl (Service Accounts)",
"source": "curl --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n --header \"Accept: application/vnd.atlas.2023-01-01+json\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{hostName}/logs/{logName}.gz?pretty=true\""
"source": "curl --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n --header \"Accept: application/vnd.atlas.2023-01-01+gzip\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{hostName}/logs/{logName}.gz\" \\\n --output \"file_name.gz\""
},
{
"lang": "cURL",
"label": "curl (Digest)",
"source": "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest \\\n --header \"Accept: application/vnd.atlas.2023-01-01+json\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{hostName}/logs/{logName}.gz?pretty=true\""
"source": "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest \\\n --header \"Accept: application/vnd.atlas.2023-01-01+gzip\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{hostName}/logs/{logName}.gz\" \\\n --output \"file_name.gz\""
}
],
"x-sunset": "2025-06-01"
Expand Down Expand Up @@ -47992,12 +47992,12 @@
{
"lang": "cURL",
"label": "curl (Service Accounts)",
"source": "curl --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n --header \"Accept: application/vnd.atlas.2023-01-01+json\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/dataFederation/{tenantName}/queryLogs.gz?pretty=true\""
"source": "curl --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n --header \"Accept: application/vnd.atlas.2023-01-01+gzip\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/dataFederation/{tenantName}/queryLogs.gz\" \\\n --output \"file_name.gz\""
},
{
"lang": "cURL",
"label": "curl (Digest)",
"source": "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest \\\n --header \"Accept: application/vnd.atlas.2023-01-01+json\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/dataFederation/{tenantName}/queryLogs.gz?pretty=true\""
"source": "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest \\\n --header \"Accept: application/vnd.atlas.2023-01-01+gzip\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/dataFederation/{tenantName}/queryLogs.gz\" \\\n --output \"file_name.gz\""
}
]
}
Expand Down Expand Up @@ -62827,12 +62827,12 @@
{
"lang": "cURL",
"label": "curl (Service Accounts)",
"source": "curl --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n --header \"Accept: application/vnd.atlas.2023-01-01+json\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/orgs/{orgId}/invoices/{invoiceId}/csv?pretty=true\""
"source": "curl --header \"Authorization: Bearer ${ACCESS_TOKEN}\" \\\n --header \"Accept: application/vnd.atlas.2023-01-01+csv\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/orgs/{orgId}/invoices/{invoiceId}/csv?pretty=true\""
},
{
"lang": "cURL",
"label": "curl (Digest)",
"source": "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest \\\n --header \"Accept: application/vnd.atlas.2023-01-01+json\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/orgs/{orgId}/invoices/{invoiceId}/csv?pretty=true\""
"source": "curl --user \"${PUBLIC_KEY}:${PRIVATE_KEY}\" \\\n --digest \\\n --header \"Accept: application/vnd.atlas.2023-01-01+csv\" \\\n -X GET \"https://cloud.mongodb.com/api/atlas/v2/orgs/{orgId}/invoices/{invoiceId}/csv?pretty=true\""
}
]
}
Expand Down
34 changes: 20 additions & 14 deletions tools/cli/test/data/split/dev/openapi-v2-2023-01-01.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37470,15 +37470,17 @@ paths:
lang: cURL
source: |-
curl --header "Authorization: Bearer ${ACCESS_TOKEN}" \
--header "Accept: application/vnd.atlas.2023-01-01+json" \
-X GET "https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/onlineArchives/queryLogs.gz?pretty=true"
--header "Accept: application/vnd.atlas.2023-01-01+gzip" \
-X GET "https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/onlineArchives/queryLogs.gz" \
--output "file_name.gz"
- label: curl (Digest)
lang: cURL
source: |-
curl --user "${PUBLIC_KEY}:${PRIVATE_KEY}" \
--digest \
--header "Accept: application/vnd.atlas.2023-01-01+json" \
-X GET "https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/onlineArchives/queryLogs.gz?pretty=true"
--header "Accept: application/vnd.atlas.2023-01-01+gzip" \
-X GET "https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/onlineArchives/queryLogs.gz" \
--output "file_name.gz"
/api/atlas/v2/groups/{groupId}/clusters/{clusterName}/outageSimulation:
delete:
description: Ends a cluster outage simulation.
Expand Down Expand Up @@ -39360,15 +39362,17 @@ paths:
lang: cURL
source: |-
curl --header "Authorization: Bearer ${ACCESS_TOKEN}" \
--header "Accept: application/vnd.atlas.2023-01-01+json" \
-X GET "https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{hostName}/logs/{logName}.gz?pretty=true"
--header "Accept: application/vnd.atlas.2023-01-01+gzip" \
-X GET "https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{hostName}/logs/{logName}.gz" \
--output "file_name.gz"
- label: curl (Digest)
lang: cURL
source: |-
curl --user "${PUBLIC_KEY}:${PRIVATE_KEY}" \
--digest \
--header "Accept: application/vnd.atlas.2023-01-01+json" \
-X GET "https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{hostName}/logs/{logName}.gz?pretty=true"
--header "Accept: application/vnd.atlas.2023-01-01+gzip" \
-X GET "https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/clusters/{hostName}/logs/{logName}.gz" \
--output "file_name.gz"
x-sunset: "2025-06-01"
/api/atlas/v2/groups/{groupId}/clusters/provider/regions:
get:
Expand Down Expand Up @@ -41412,15 +41416,17 @@ paths:
lang: cURL
source: |-
curl --header "Authorization: Bearer ${ACCESS_TOKEN}" \
--header "Accept: application/vnd.atlas.2023-01-01+json" \
-X GET "https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/dataFederation/{tenantName}/queryLogs.gz?pretty=true"
--header "Accept: application/vnd.atlas.2023-01-01+gzip" \
-X GET "https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/dataFederation/{tenantName}/queryLogs.gz" \
--output "file_name.gz"
- label: curl (Digest)
lang: cURL
source: |-
curl --user "${PUBLIC_KEY}:${PRIVATE_KEY}" \
--digest \
--header "Accept: application/vnd.atlas.2023-01-01+json" \
-X GET "https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/dataFederation/{tenantName}/queryLogs.gz?pretty=true"
--header "Accept: application/vnd.atlas.2023-01-01+gzip" \
-X GET "https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/dataFederation/{tenantName}/queryLogs.gz" \
--output "file_name.gz"
/api/atlas/v2/groups/{groupId}/databaseUsers:
get:
description: Returns all database users that belong to the specified project. To use this resource, the requesting API Key must have the Project Read Only role.
Expand Down Expand Up @@ -55868,14 +55874,14 @@ paths:
lang: cURL
source: |-
curl --header "Authorization: Bearer ${ACCESS_TOKEN}" \
--header "Accept: application/vnd.atlas.2023-01-01+json" \
--header "Accept: application/vnd.atlas.2023-01-01+csv" \
-X GET "https://cloud.mongodb.com/api/atlas/v2/orgs/{orgId}/invoices/{invoiceId}/csv?pretty=true"
- label: curl (Digest)
lang: cURL
source: |-
curl --user "${PUBLIC_KEY}:${PRIVATE_KEY}" \
--digest \
--header "Accept: application/vnd.atlas.2023-01-01+json" \
--header "Accept: application/vnd.atlas.2023-01-01+csv" \
-X GET "https://cloud.mongodb.com/api/atlas/v2/orgs/{orgId}/invoices/{invoiceId}/csv?pretty=true"
/api/atlas/v2/orgs/{orgId}/invoices/pending:
get:
Expand Down
Loading
Loading