From 003da31b34c88f2b48b3445ea87d1be87b672796 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 8 Mar 2024 14:24:57 +0000 Subject: [PATCH 1/9] CLOUDP-236398: include the merging logic for paths --- tools/cli/go.mod | 3 +- tools/cli/go.sum | 8 ++ .../openapi/errors/pathConflictError.go | 11 +++ tools/cli/internal/openapi/oasdiff.go | 74 +++++++++++++++++++ tools/cli/internal/openapi/openapi.go | 13 ++++ tools/cli/internal/openapi/openapi3.go | 31 ++++++++ 6 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 tools/cli/internal/openapi/errors/pathConflictError.go create mode 100644 tools/cli/internal/openapi/oasdiff.go create mode 100644 tools/cli/internal/openapi/openapi.go create mode 100644 tools/cli/internal/openapi/openapi3.go diff --git a/tools/cli/go.mod b/tools/cli/go.mod index 6ff94b0..099c294 100644 --- a/tools/cli/go.mod +++ b/tools/cli/go.mod @@ -3,12 +3,13 @@ module mongodb/openapi/tools/cli go 1.22.1 require ( + github.com/getkin/kin-openapi v0.120.0 github.com/spf13/cobra v1.8.0 github.com/tufin/oasdiff v1.9.5 ) require ( - github.com/getkin/kin-openapi v0.120.0 // indirect + cloud.google.com/go v0.110.10 // indirect github.com/go-openapi/jsonpointer v0.20.0 // indirect github.com/go-openapi/swag v0.22.4 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect diff --git a/tools/cli/go.sum b/tools/cli/go.sum index d89a2c1..3fcc8a5 100644 --- a/tools/cli/go.sum +++ b/tools/cli/go.sum @@ -1,3 +1,7 @@ +cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= +cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= +github.com/TwiN/go-color v1.4.1 h1:mqG0P/KBgHKVqmtL5ye7K0/Gr4l6hTksPgTgMk3mUzc= +github.com/TwiN/go-color v1.4.1/go.mod h1:WcPf/jtiW95WBIsEeY1Lc/b8aaWoiqQpu5cf8WFxu+s= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -9,6 +13,8 @@ github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogB github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= @@ -42,6 +48,8 @@ github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0 github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc= github.com/yargevad/filepathx v1.0.0/go.mod h1:BprfX/gpYNJHJfc35GjRRpVcwWXS89gGulUIU5tK3tA= +golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w= +golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/tools/cli/internal/openapi/errors/pathConflictError.go b/tools/cli/internal/openapi/errors/pathConflictError.go new file mode 100644 index 0000000..59fd5b8 --- /dev/null +++ b/tools/cli/internal/openapi/errors/pathConflictError.go @@ -0,0 +1,11 @@ +package errors + +import "fmt" + +type PathConflictError struct { + Entry string +} + +func (e PathConflictError) Error() string { + return fmt.Sprintf("there was a conflict with the Path: %s", e.Entry) +} diff --git a/tools/cli/internal/openapi/oasdiff.go b/tools/cli/internal/openapi/oasdiff.go new file mode 100644 index 0000000..07f4611 --- /dev/null +++ b/tools/cli/internal/openapi/oasdiff.go @@ -0,0 +1,74 @@ +package openapi + +import ( + "fmt" + "mongodb/openapi/tools/cli/internal/openapi/errors" + "os" + + "github.com/tufin/oasdiff/diff" + "github.com/tufin/oasdiff/load" +) + +type OasDiff struct { + base *load.SpecInfo + external *load.SpecInfo + config *diff.Config + specDiff *diff.Diff + parser Parser +} + +func (o *OasDiff) Merge(paths []string) (*load.SpecInfo, error) { + for _, p := range paths { + spec, err := o.parser.CreateOpenAPISpecFromPath(p) + if err != nil { + return nil, err + } + + o.config = &diff.Config{ + IncludePathParams: true, + } + + specDiff, err := diff.Get(o.config, o.base.Spec, spec.Spec) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "error in calculating the diff of the specs: %s", err) + return nil, err + } + + o.specDiff = specDiff + o.external = spec + err = o.mergeSpecIntoBase() + if err != nil { + return nil, err + } + } + + return o.base, nil +} + +func (o OasDiff) mergeSpecIntoBase() error { + return o.mergePaths() +} + +func (o OasDiff) mergePaths() error { + pathsToMerge := o.external.Spec.Paths + basePaths := o.base.Spec.Paths + for k, v := range pathsToMerge { + if _, ok := basePaths[k]; !ok { + basePaths[k] = v + } else { + return errors.PathConflictError{ + Entry: k, + } + } + } + + o.base.Spec.Paths = basePaths + return nil +} + +func NewOasDiff(base *load.SpecInfo) *OasDiff { + return &OasDiff{ + base: base, + parser: NewOpenAPI3(), + } +} diff --git a/tools/cli/internal/openapi/openapi.go b/tools/cli/internal/openapi/openapi.go new file mode 100644 index 0000000..77dc308 --- /dev/null +++ b/tools/cli/internal/openapi/openapi.go @@ -0,0 +1,13 @@ +package openapi + +import ( + "github.com/tufin/oasdiff/load" +) + +type Merger interface { + Merge([]string) (*load.SpecInfo, error) +} + +type Parser interface { + CreateOpenAPISpecFromPath(string) (*load.SpecInfo, error) +} diff --git a/tools/cli/internal/openapi/openapi3.go b/tools/cli/internal/openapi/openapi3.go new file mode 100644 index 0000000..3f5bef0 --- /dev/null +++ b/tools/cli/internal/openapi/openapi3.go @@ -0,0 +1,31 @@ +package openapi + +import ( + "github.com/getkin/kin-openapi/openapi3" + "github.com/tufin/oasdiff/load" +) + +type OpenAPI3 struct { + IsExternalRefsAllowed bool + CircularReferenceCounter int +} + +func (o *OpenAPI3) CreateOpenAPISpecFromPath(path string) (*load.SpecInfo, error) { + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + openapi3.CircularReferenceCounter = 10 + + spec, err := load.LoadSpecInfo(loader, load.NewSource(path)) + if err != nil { + return nil, err + } + + return spec, nil +} + +func NewOpenAPI3() *OpenAPI3 { + return &OpenAPI3{ + IsExternalRefsAllowed: true, + CircularReferenceCounter: 10, + } +} From 628f4d2abfe675d84177f376fb34afe752489f95 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 8 Mar 2024 14:27:24 +0000 Subject: [PATCH 2/9] refactoring --- tools/cli/internal/openapi/oasdiff.go | 2 +- tools/cli/internal/openapi/openapi.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/cli/internal/openapi/oasdiff.go b/tools/cli/internal/openapi/oasdiff.go index 07f4611..b8ebf25 100644 --- a/tools/cli/internal/openapi/oasdiff.go +++ b/tools/cli/internal/openapi/oasdiff.go @@ -17,7 +17,7 @@ type OasDiff struct { parser Parser } -func (o *OasDiff) Merge(paths []string) (*load.SpecInfo, error) { +func (o *OasDiff) MergeOpenAPISpecs(paths []string) (*load.SpecInfo, error) { for _, p := range paths { spec, err := o.parser.CreateOpenAPISpecFromPath(p) if err != nil { diff --git a/tools/cli/internal/openapi/openapi.go b/tools/cli/internal/openapi/openapi.go index 77dc308..5bdb959 100644 --- a/tools/cli/internal/openapi/openapi.go +++ b/tools/cli/internal/openapi/openapi.go @@ -5,7 +5,7 @@ import ( ) type Merger interface { - Merge([]string) (*load.SpecInfo, error) + MergeOpenAPISpecs([]string) (*load.SpecInfo, error) } type Parser interface { From 7efff49c7a283ebef531f90b3aa370d13038f780 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 8 Mar 2024 15:45:52 +0000 Subject: [PATCH 3/9] CLOUDP-236398: include the merging logic for paths --- tools/cli/internal/openapi/oasdiff.go | 74 ++++++++++++++++++++++++++ tools/cli/internal/openapi/openapi3.go | 31 +++++++++++ 2 files changed, 105 insertions(+) create mode 100644 tools/cli/internal/openapi/oasdiff.go create mode 100644 tools/cli/internal/openapi/openapi3.go diff --git a/tools/cli/internal/openapi/oasdiff.go b/tools/cli/internal/openapi/oasdiff.go new file mode 100644 index 0000000..be42623 --- /dev/null +++ b/tools/cli/internal/openapi/oasdiff.go @@ -0,0 +1,74 @@ +package openapi + +import ( + "fmt" + "mongodb/openapi/tools/cli/internal/openapi/errors" + "os" + + "github.com/tufin/oasdiff/diff" + "github.com/tufin/oasdiff/load" +) + +type OasDiff struct { + base *load.SpecInfo + external *load.SpecInfo + config *diff.Config + specDiff *diff.Diff + parser Parser +} + +func NewOasDiff(base *load.SpecInfo) *OasDiff { + return &OasDiff{ + base: base, + parser: NewOpenAPI3(), + } +} + +func (o *OasDiff) MergeOpenAPISpecs(paths []string) (*load.SpecInfo, error) { + for _, p := range paths { + spec, err := o.parser.CreateOpenAPISpecFromPath(p) + if err != nil { + return nil, err + } + + o.config = &diff.Config{ + IncludePathParams: true, + } + + specDiff, err := diff.Get(o.config, o.base.Spec, spec.Spec) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "error in calculating the diff of the specs: %s", err) + return nil, err + } + + o.specDiff = specDiff + o.external = spec + err = o.mergeSpecIntoBase() + if err != nil { + return nil, err + } + } + + return o.base, nil +} + +func (o OasDiff) mergeSpecIntoBase() error { + return o.mergePaths() +} + +func (o OasDiff) mergePaths() error { + pathsToMerge := o.external.Spec.Paths + basePaths := o.base.Spec.Paths + for k, v := range pathsToMerge { + if _, ok := basePaths[k]; !ok { + basePaths[k] = v + } else { + return errors.PathConflictError{ + Entry: k, + } + } + } + + o.base.Spec.Paths = basePaths + return nil +} diff --git a/tools/cli/internal/openapi/openapi3.go b/tools/cli/internal/openapi/openapi3.go new file mode 100644 index 0000000..840dbfc --- /dev/null +++ b/tools/cli/internal/openapi/openapi3.go @@ -0,0 +1,31 @@ +package openapi + +import ( + "github.com/getkin/kin-openapi/openapi3" + "github.com/tufin/oasdiff/load" +) + +type OpenAPI3 struct { + IsExternalRefsAllowed bool + CircularReferenceCounter int +} + +func NewOpenAPI3() *OpenAPI3 { + return &OpenAPI3{ + IsExternalRefsAllowed: true, + CircularReferenceCounter: 10, + } +} + +func (o *OpenAPI3) CreateOpenAPISpecFromPath(path string) (*load.SpecInfo, error) { + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + openapi3.CircularReferenceCounter = 10 + + spec, err := load.LoadSpecInfo(loader, load.NewSource(path)) + if err != nil { + return nil, err + } + + return spec, nil +} From b3118e1cc96e6fc16e6071ff376efce9cd68eec4 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 8 Mar 2024 16:04:10 +0000 Subject: [PATCH 4/9] improvements --- tools/cli/internal/cli/merge/merge.go | 19 ++++++++++++++++--- tools/cli/internal/openapi/oasdiff.go | 19 ++++++++++++------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/tools/cli/internal/cli/merge/merge.go b/tools/cli/internal/cli/merge/merge.go index 4f48bdc..1fe14e6 100644 --- a/tools/cli/internal/cli/merge/merge.go +++ b/tools/cli/internal/cli/merge/merge.go @@ -19,6 +19,7 @@ import ( "log" "mongodb/openapi/tools/cli/internal/cli/flag" "mongodb/openapi/tools/cli/internal/cli/usage" + "mongodb/openapi/tools/cli/internal/openapi" "os" "github.com/spf13/cobra" @@ -29,14 +30,24 @@ const ( ) type Opts struct { + Merger openapi.Merger basePath string outputPath string externalPaths []string } func (o *Opts) Run(_ []string) error { - // To add in follow up PR: CLOUDP-225849 - return o.saveFile([]byte("test")) + federated, err := o.Merger.MergeOpenAPISpecs(o.externalPaths) + if err != nil { + return err + } + + federatedBytes, err := federated.Spec.MarshalJSON() + if err != nil { + return err + } + + return o.saveFile(federatedBytes) } func (o *Opts) PreRunE(_ []string) error { @@ -48,7 +59,9 @@ func (o *Opts) PreRunE(_ []string) error { return fmt.Errorf("no external OAS detected. Please, use the flag %s to include at least one OAS", flag.External) } - return nil + m, err := openapi.NewOasDiff(o.basePath) + o.Merger = m + return err } func (o *Opts) saveFile(data []byte) error { diff --git a/tools/cli/internal/openapi/oasdiff.go b/tools/cli/internal/openapi/oasdiff.go index be42623..734f766 100644 --- a/tools/cli/internal/openapi/oasdiff.go +++ b/tools/cli/internal/openapi/oasdiff.go @@ -1,9 +1,8 @@ package openapi import ( - "fmt" + "log" "mongodb/openapi/tools/cli/internal/openapi/errors" - "os" "github.com/tufin/oasdiff/diff" "github.com/tufin/oasdiff/load" @@ -17,11 +16,17 @@ type OasDiff struct { parser Parser } -func NewOasDiff(base *load.SpecInfo) *OasDiff { - return &OasDiff{ - base: base, - parser: NewOpenAPI3(), +func NewOasDiff(base string) (*OasDiff, error) { + parser := NewOpenAPI3() + baseSpec, err := parser.CreateOpenAPISpecFromPath(base) + if err != nil { + return nil, err } + + return &OasDiff{ + base: baseSpec, + parser: parser, + }, nil } func (o *OasDiff) MergeOpenAPISpecs(paths []string) (*load.SpecInfo, error) { @@ -37,7 +42,7 @@ func (o *OasDiff) MergeOpenAPISpecs(paths []string) (*load.SpecInfo, error) { specDiff, err := diff.Get(o.config, o.base.Spec, spec.Spec) if err != nil { - _, _ = fmt.Fprintf(os.Stderr, "error in calculating the diff of the specs: %s", err) + log.Fatalf("error in calculating the diff of the specs: %s", err) return nil, err } From 15c66d0c57858fe786dfbcfbdf8cdac02ce0759e Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 8 Mar 2024 16:05:01 +0000 Subject: [PATCH 5/9] Update openapi3.go --- tools/cli/internal/openapi/openapi3.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/cli/internal/openapi/openapi3.go b/tools/cli/internal/openapi/openapi3.go index 840dbfc..a896434 100644 --- a/tools/cli/internal/openapi/openapi3.go +++ b/tools/cli/internal/openapi/openapi3.go @@ -19,9 +19,6 @@ func NewOpenAPI3() *OpenAPI3 { func (o *OpenAPI3) CreateOpenAPISpecFromPath(path string) (*load.SpecInfo, error) { loader := openapi3.NewLoader() - loader.IsExternalRefsAllowed = true - openapi3.CircularReferenceCounter = 10 - spec, err := load.LoadSpecInfo(loader, load.NewSource(path)) if err != nil { return nil, err From 9b4aa5f530e5dbcf6f354eb25ff83974fa450cfe Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 8 Mar 2024 17:43:01 +0000 Subject: [PATCH 6/9] Added test data --- tools/cli/FOAS.json | 434 ++++++++++++ tools/cli/Makefile | 1 + tools/cli/internal/openapi/oasdiff.go | 7 +- tools/cli/internal/openapi/openapi3.go | 3 + tools/cli/test/data/FOAS.json | 479 +++++++++++++ tools/cli/test/data/base.json | 441 ++++++++++++ tools/cli/test/data/external.json | 899 +++++++++++++++++++++++++ 7 files changed, 2260 insertions(+), 4 deletions(-) create mode 100644 tools/cli/FOAS.json create mode 100644 tools/cli/test/data/FOAS.json create mode 100644 tools/cli/test/data/base.json create mode 100644 tools/cli/test/data/external.json diff --git a/tools/cli/FOAS.json b/tools/cli/FOAS.json new file mode 100644 index 0000000..7527ffc --- /dev/null +++ b/tools/cli/FOAS.json @@ -0,0 +1,434 @@ +{ + "components": { + "parameters": { + "envelope": { + "description": "Flag that indicates whether Application wraps the response in an `envelope` JSON object. Some API clients cannot access the HTTP response headers or status code. To remediate this, set envelope=true in the query. Endpoints that return a list of results use the results object as an envelope. Application adds the status parameter to the response body.", + "in": "query", + "name": "envelope", + "schema": { + "default": false, + "example": false, + "type": "boolean" + } + }, + "pretty": { + "description": "Flag that indicates whether the response body should be in the prettyprint format.", + "in": "query", + "name": "pretty", + "schema": { + "default": false, + "example": false, + "type": "boolean" + } + } + }, + "responses": { + "accepted": { + "description": "Accepted." + }, + "badRequest": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint) No provider AWS exists.", + "error": 400, + "errorCode": "INVALID_PROVIDER", + "parameters": [ + "AWS" + ], + "reason": "Bad Request" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Bad Request." + }, + "conflict": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint) Cannot delete organization link while there is active migration in following project ids: 60c4fd418ebe251047c50554", + "error": 409, + "errorCode": "CANNOT_DELETE_ORG_LINK_WITH_RUNNING_LIVE_EXPORT", + "parameters": [ + "60c4fd418ebe251047c50554" + ], + "reason": "Conflict" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Conflict." + }, + "forbidden": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint)", + "error": 403, + "errorCode": "CANNOT_CHANGE_GROUP_NAME", + "parameters": [ + "EXAMPLE" + ], + "reason": "Forbidden" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Forbidden." + }, + "gone": { + "content": { + "application/json": { + "example": { + "detail": "This happens when a resource is marked for sunset and the sunset date is in the past.", + "error": 410, + "errorCode": "VERSION_GONE", + "parameters": [ + "EXAMPLE" + ], + "reason": "Gone" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Gone." + }, + "internalServerError": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint)", + "error": 500, + "errorCode": "UNEXPECTED_ERROR", + "parameters": [ + "EXAMPLE" + ], + "reason": "Internal Server Error" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Internal Server Error." + }, + "methodNotAllowed": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint)", + "error": 405, + "errorCode": "ATLAS_BACKUP_CANCEL_SHARD_RESTORE_JOB_NOT_ALLOWED", + "parameters": [ + "EXAMPLE" + ], + "reason": "Method Not Allowed" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Method Not Allowed." + }, + "noBody": { + "description": "This endpoint does not return a response body." + }, + "notFound": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint) Cannot find resource AWS", + "error": 404, + "errorCode": "RESOURCE_NOT_FOUND", + "parameters": [ + "AWS" + ], + "reason": "Not Found" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not Found." + }, + "paymentRequired": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint)", + "error": 402, + "errorCode": "NO_PAYMENT_INFORMATION_FOUND", + "parameters": [ + "EXAMPLE" + ], + "reason": "Payment Required" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Payment Required." + }, + "unauthorized": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint)", + "error": 401, + "errorCode": "NOT_ORG_GROUP_CREATOR", + "parameters": [ + "EXAMPLE" + ], + "reason": "Unauthorized" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Unauthorized." + } + }, + "schemas": { + "ApiError": { + "properties": { + "detail": { + "description": "Describes the specific conditions or reasons that cause each type of error.", + "type": "string" + }, + "error": { + "description": "HTTP status code returned with this error.", + "format": "int32", + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "errorCode": { + "description": "Application error code returned with this error.", + "example": "TOO_MANY_GROUP_NOTIFICATIONS", + "type": "string" + }, + "parameters": { + "description": "Parameter uses to give more information about the error.", + "type": "array" + }, + "reason": { + "description": "Application error message returned with this error.", + "example": "At most one group notification can be specified for an alert configuration.", + "type": "string" + } + }, + "type": "object" + }, + "ApiKey": { + "description": "Details contained in one API key.", + "nullable": true, + "properties": { + "id": { + "description": "Unique 24-hexadecimal digit string that identifies this organization API key.", + "example": "32b6e34b3d91647abb20e7b8", + "maxLength": 24, + "minLength": 24, + "pattern": "^([a-f0-9]{24})$", + "readOnly": true, + "type": "string" + }, + "publicKey": { + "description": "Public API key value set for the specified organization API key.", + "maxLength": 8, + "minLength": 8, + "readOnly": true, + "type": "string" + } + }, + "readOnly": true, + "required": [ + "id", + "publicKey" + ], + "type": "object" + }, + "Link": { + "properties": { + "href": { + "description": "Uniform Resource Locator (URL) that points another API resource to which this response has some relationship. This URL often begins with `https://cloud.mongodb.com/api/atlas`.", + "example": "https://cloud.mongodb.com/api/atlas", + "type": "string" + }, + "rel": { + "description": "Uniform Resource Locator (URL) that defines the semantic relationship between this resource and another API resource. This URL often begins with `https://cloud.mongodb.com/api/atlas`.", + "example": "self", + "type": "string" + } + }, + "type": "object" + }, + "SystemStatus": { + "properties": { + "apiKey": { + "$ref": "#/components/schemas/ApiKey" + }, + "appName": { + "description": "Human-readable label that identifies the service from which you requested this response.", + "enum": [ + "MongoDB Atlas" + ], + "readOnly": true, + "type": "string" + }, + "build": { + "description": "Unique 40-hexadecimal digit hash that identifies the latest git commit merged for this application.", + "example": "83be55e140f493c88e7f578aae96548dd881587b", + "readOnly": true, + "type": "string" + }, + "links": { + "description": "List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships.", + "externalDocs": { + "description": "Web Linking Specification (RFC 5988)", + "url": "https://datatracker.ietf.org/doc/html/rfc5988" + }, + "items": { + "$ref": "#/components/schemas/Link" + }, + "readOnly": true, + "type": "array" + }, + "throttling": { + "description": "Flag that indicates whether someone enabled throttling on this service.", + "readOnly": true, + "type": "boolean" + } + }, + "required": [ + "apiKey", + "appName", + "build", + "throttling" + ], + "type": "object" + } + } + }, + "info": { + "description": "The MongoDB Atlas Administration API allows developers to manage all components in MongoDB Atlas.\n\nThe Atlas Administration API uses HTTP Digest Authentication to authenticate requests. Provide a programmatic API public key and corresponding private key as the username and password when constructing the HTTP request. For example, to [return database access history](#tag/Access-Tracking/operation/listAccessLogsByClusterName) with [cURL](https://en.wikipedia.org/wiki/CURL), run the following command in the terminal:\n\n```\ncurl --user \"{PUBLIC-KEY}:{PRIVATE-KEY}\" \\\n --digest \\\n --header \"Accept: application/vnd.atlas.2023-02-01+json\" \\\n GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/dbAccessHistory/clusters/{clusterName}?pretty=true\"\n```\n\nTo learn more, see [Get Started with the Atlas Administration API](https://www.mongodb.com/docs/atlas/configure-api-access/). For support, see [MongoDB Support](https://www.mongodb.com/support/get-started).", + "license": { + "name": "CC BY-NC-SA 3.0 US", + "url": "https://creativecommons.org/licenses/by-nc-sa/3.0/us/" + }, + "termsOfService": "https://www.mongodb.com/mongodb-management-service-terms-and-conditions", + "title": "MongoDB Atlas Administration API", + "version": "2.0", + "x-xgen-sha": "7bcbc2297ef0216fabc79c752cf21b132fc76577" + }, + "openapi": "3.0.1", + "paths": { + "/api/atlas/v2": { + "get": { + "description": "This resource returns information about the MongoDB application along with API key meta data.", + "operationId": "getSystemStatus", + "parameters": [ + { + "$ref": "#/components/parameters/envelope" + }, + { + "$ref": "#/components/parameters/pretty" + } + ], + "responses": { + "200": { + "content": { + "application/vnd.atlas.2023-01-01+json": { + "schema": { + "$ref": "#/components/schemas/SystemStatus" + }, + "x-xgen-version": "2023-01-01" + } + }, + "description": "OK" + }, + "401": { + "$ref": "#/components/responses/unauthorized" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "500": { + "$ref": "#/components/responses/internalServerError" + } + }, + "summary": "Return the status of this MongoDB application", + "tags": [ + "Root" + ] + } + }, + "/api/atlas/v2/search/test": { + "get": { + "description": "This resource returns information about the MongoDB application along with API key meta data.", + "operationId": "getSystemStatus", + "parameters": [ + { + "$ref": "#/components/parameters/envelope" + }, + { + "$ref": "#/components/parameters/pretty" + } + ], + "responses": { + "200": { + "content": { + "application/vnd.atlas.2023-01-01+json": { + "schema": { + "$ref": "base.json#/components/schemas/SystemStatus" + }, + "x-xgen-version": "2023-01-01" + } + }, + "description": "OK" + }, + "401": { + "$ref": "base.json#/components/responses/unauthorized" + }, + "404": { + "$ref": "base.json#/components/responses/notFound" + }, + "500": { + "$ref": "base.json#/components/responses/internalServerError" + } + }, + "summary": "Return the status of this MongoDB application", + "tags": [ + "Search Test" + ] + } + } + }, + "servers": [ + { + "url": "https://cloud.mongodb.com" + } + ], + "tags": [ + { + "description": "Returns access logs for authentication attempts made to Atlas database deployments. To view database access history, you must have either the Project Owner or Organization Owner role.", + "name": "Access Tracking" + }, + { + "description": "Returns and edits the conditions that trigger alerts and how MongoDB Cloud notifies users. This collection remains under revision and may change.", + "name": "Alert Configurations" + } + ] +} diff --git a/tools/cli/Makefile b/tools/cli/Makefile index 8dc6f04..044821a 100644 --- a/tools/cli/Makefile +++ b/tools/cli/Makefile @@ -24,6 +24,7 @@ deps: ## Download go module dependencies .PHONY: devtools devtools: ## Install dev tools @echo "==> Installing dev tools..." + go install go.uber.org/mock/mockgen@latest curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin $(GOLANGCI_VERSION) .PHONY: setup diff --git a/tools/cli/internal/openapi/oasdiff.go b/tools/cli/internal/openapi/oasdiff.go index 734f766..eec993b 100644 --- a/tools/cli/internal/openapi/oasdiff.go +++ b/tools/cli/internal/openapi/oasdiff.go @@ -26,6 +26,9 @@ func NewOasDiff(base string) (*OasDiff, error) { return &OasDiff{ base: baseSpec, parser: parser, + config: &diff.Config{ + IncludePathParams: true, + }, }, nil } @@ -36,10 +39,6 @@ func (o *OasDiff) MergeOpenAPISpecs(paths []string) (*load.SpecInfo, error) { return nil, err } - o.config = &diff.Config{ - IncludePathParams: true, - } - specDiff, err := diff.Get(o.config, o.base.Spec, spec.Spec) if err != nil { log.Fatalf("error in calculating the diff of the specs: %s", err) diff --git a/tools/cli/internal/openapi/openapi3.go b/tools/cli/internal/openapi/openapi3.go index a896434..014d6bc 100644 --- a/tools/cli/internal/openapi/openapi3.go +++ b/tools/cli/internal/openapi/openapi3.go @@ -18,7 +18,10 @@ func NewOpenAPI3() *OpenAPI3 { } func (o *OpenAPI3) CreateOpenAPISpecFromPath(path string) (*load.SpecInfo, error) { + openapi3.CircularReferenceCounter = o.CircularReferenceCounter loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = o.IsExternalRefsAllowed + spec, err := load.LoadSpecInfo(loader, load.NewSource(path)) if err != nil { return nil, err diff --git a/tools/cli/test/data/FOAS.json b/tools/cli/test/data/FOAS.json new file mode 100644 index 0000000..8a2e536 --- /dev/null +++ b/tools/cli/test/data/FOAS.json @@ -0,0 +1,479 @@ +{ + "components": { + "parameters": { + "envelope": { + "description": "Flag that indicates whether Application wraps the response in an `envelope` JSON object. Some API clients cannot access the HTTP response headers or status code. To remediate this, set envelope=true in the query. Endpoints that return a list of results use the results object as an envelope. Application adds the status parameter to the response body.", + "in": "query", + "name": "envelope", + "schema": { + "default": false, + "example": false, + "type": "boolean" + } + }, + "pretty": { + "description": "Flag that indicates whether the response body should be in the prettyprint format.", + "in": "query", + "name": "pretty", + "schema": { + "default": false, + "example": false, + "type": "boolean" + } + } + }, + "responses": { + "accepted": { + "description": "Accepted." + }, + "badRequest": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint) No provider AWS exists.", + "error": 400, + "errorCode": "INVALID_PROVIDER", + "parameters": [ + "AWS" + ], + "reason": "Bad Request" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Bad Request." + }, + "conflict": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint) Cannot delete organization link while there is active migration in following project ids: 60c4fd418ebe251047c50554", + "error": 409, + "errorCode": "CANNOT_DELETE_ORG_LINK_WITH_RUNNING_LIVE_EXPORT", + "parameters": [ + "60c4fd418ebe251047c50554" + ], + "reason": "Conflict" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Conflict." + }, + "forbidden": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint)", + "error": 403, + "errorCode": "CANNOT_CHANGE_GROUP_NAME", + "parameters": [ + "EXAMPLE" + ], + "reason": "Forbidden" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Forbidden." + }, + "gone": { + "content": { + "application/json": { + "example": { + "detail": "This happens when a resource is marked for sunset and the sunset date is in the past.", + "error": 410, + "errorCode": "VERSION_GONE", + "parameters": [ + "EXAMPLE" + ], + "reason": "Gone" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Gone." + }, + "internalServerError": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint)", + "error": 500, + "errorCode": "UNEXPECTED_ERROR", + "parameters": [ + "EXAMPLE" + ], + "reason": "Internal Server Error" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Internal Server Error." + }, + "methodNotAllowed": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint)", + "error": 405, + "errorCode": "ATLAS_BACKUP_CANCEL_SHARD_RESTORE_JOB_NOT_ALLOWED", + "parameters": [ + "EXAMPLE" + ], + "reason": "Method Not Allowed" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Method Not Allowed." + }, + "noBody": { + "description": "This endpoint does not return a response body." + }, + "notFound": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint) Cannot find resource AWS", + "error": 404, + "errorCode": "RESOURCE_NOT_FOUND", + "parameters": [ + "AWS" + ], + "reason": "Not Found" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not Found." + }, + "paymentRequired": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint)", + "error": 402, + "errorCode": "NO_PAYMENT_INFORMATION_FOUND", + "parameters": [ + "EXAMPLE" + ], + "reason": "Payment Required" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Payment Required." + }, + "unauthorized": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint)", + "error": 401, + "errorCode": "NOT_ORG_GROUP_CREATOR", + "parameters": [ + "EXAMPLE" + ], + "reason": "Unauthorized" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Unauthorized." + } + }, + "schemas": { + "ApiError": { + "properties": { + "detail": { + "description": "Describes the specific conditions or reasons that cause each type of error.", + "type": "string" + }, + "error": { + "description": "HTTP status code returned with this error.", + "format": "int32", + "maximum": 599, + "minimum": 200, + "type": "integer" + }, + "errorCode": { + "description": "Application error code returned with this error.", + "example": "TOO_MANY_GROUP_NOTIFICATIONS", + "type": "string" + }, + "parameters": { + "description": "Parameter uses to give more information about the error.", + "type": "array" + }, + "reason": { + "description": "Application error message returned with this error.", + "example": "At most one group notification can be specified for an alert configuration.", + "type": "string" + } + }, + "type": "object" + }, + "ApiKey": { + "description": "Details contained in one API key.", + "nullable": true, + "properties": { + "id": { + "description": "Unique 24-hexadecimal digit string that identifies this organization API key.", + "example": "32b6e34b3d91647abb20e7b8", + "maxLength": 24, + "minLength": 24, + "pattern": "^([a-f0-9]{24})$", + "readOnly": true, + "type": "string" + }, + "publicKey": { + "description": "Public API key value set for the specified organization API key.", + "maxLength": 8, + "minLength": 8, + "readOnly": true, + "type": "string" + } + }, + "readOnly": true, + "required": [ + "id", + "publicKey" + ], + "type": "object" + }, + "Link": { + "properties": { + "href": { + "description": "Uniform Resource Locator (URL) that points another API resource to which this response has some relationship. This URL often begins with `https://cloud.mongodb.com/api/atlas`.", + "example": "https://cloud.mongodb.com/api/atlas", + "type": "string" + }, + "rel": { + "description": "Uniform Resource Locator (URL) that defines the semantic relationship between this resource and another API resource. This URL often begins with `https://cloud.mongodb.com/api/atlas`.", + "example": "self", + "type": "string" + } + }, + "type": "object" + }, + "SystemStatus": { + "properties": { + "apiKey": { + "$ref": "#/components/schemas/ApiKey" + }, + "appName": { + "description": "Human-readable label that identifies the service from which you requested this response.", + "enum": [ + "MongoDB Atlas" + ], + "readOnly": true, + "type": "string" + }, + "build": { + "description": "Unique 40-hexadecimal digit hash that identifies the latest git commit merged for this application.", + "example": "83be55e140f493c88e7f578aae96548dd881587b", + "readOnly": true, + "type": "string" + }, + "links": { + "description": "List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships.", + "externalDocs": { + "description": "Web Linking Specification (RFC 5988)", + "url": "https://datatracker.ietf.org/doc/html/rfc5988" + }, + "items": { + "$ref": "#/components/schemas/Link" + }, + "readOnly": true, + "type": "array" + }, + "throttling": { + "description": "Flag that indicates whether someone enabled throttling on this service.", + "readOnly": true, + "type": "boolean" + } + }, + "required": [ + "apiKey", + "appName", + "build", + "throttling" + ], + "type": "object" + } + } + }, + "info": { + "description": "The MongoDB Atlas Administration API allows developers to manage all components in MongoDB Atlas.\n\nThe Atlas Administration API uses HTTP Digest Authentication to authenticate requests. Provide a programmatic API public key and corresponding private key as the username and password when constructing the HTTP request. For example, to [return database access history](#tag/Access-Tracking/operation/listAccessLogsByClusterName) with [cURL](https://en.wikipedia.org/wiki/CURL), run the following command in the terminal:\n\n```\ncurl --user \"{PUBLIC-KEY}:{PRIVATE-KEY}\" \\\n --digest \\\n --header \"Accept: application/vnd.atlas.2023-02-01+json\" \\\n GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/dbAccessHistory/clusters/{clusterName}?pretty=true\"\n```\n\nTo learn more, see [Get Started with the Atlas Administration API](https://www.mongodb.com/docs/atlas/configure-api-access/). For support, see [MongoDB Support](https://www.mongodb.com/support/get-started).", + "license": { + "name": "CC BY-NC-SA 3.0 US", + "url": "https://creativecommons.org/licenses/by-nc-sa/3.0/us/" + }, + "termsOfService": "https://www.mongodb.com/mongodb-management-service-terms-and-conditions", + "title": "MongoDB Atlas Administration API", + "version": "2.0", + "x-xgen-sha": "7bcbc2297ef0216fabc79c752cf21b132fc76577" + }, + "openapi": "3.0.1", + "paths": { + "/api/atlas/v2": { + "get": { + "description": "This resource returns information about the MongoDB application along with API key meta data.", + "operationId": "getSystemStatus", + "parameters": [ + { + "$ref": "#/components/parameters/envelope" + }, + { + "$ref": "#/components/parameters/pretty" + } + ], + "responses": { + "200": { + "content": { + "application/vnd.atlas.2023-01-01+json": { + "schema": { + "$ref": "#/components/schemas/SystemStatus" + }, + "x-xgen-version": "2023-01-01" + } + }, + "description": "OK" + }, + "401": { + "$ref": "#/components/responses/unauthorized" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "500": { + "$ref": "#/components/responses/internalServerError" + } + }, + "summary": "Return the status of this MongoDB application", + "tags": [ + "Root" + ] + } + }, + "/api/atlas/v2/alertConfigs/matchers/fieldNames": { + "get": { + "description": "Get all field names that the `matchers.fieldName` parameter accepts when you create or update an Alert Configuration. You can successfully call this endpoint with any assigned role.", + "operationId": "listAlertConfigurationMatchersFieldNames", + "parameters": [ + { + "$ref": "#/components/parameters/envelope" + }, + { + "$ref": "#/components/parameters/pretty" + } + ], + "responses": { + "200": { + "content": { + "application/vnd.atlas.2023-01-01+json": { + "schema": { + "items": { + "$ref": "external.json#/components/schemas/ApiAtlasFTSAnalyzersViewManual" + }, + "type": "array" + }, + "x-xgen-version": "2023-01-01" + } + }, + "description": "OK" + }, + "401": { + "$ref": "#/components/responses/unauthorized" + }, + "500": { + "$ref": "#/components/responses/internalServerError" + } + }, + "security": [ + { + "DigestAuth": [] + } + ], + "summary": "Get All Alert Configuration Matchers Field Names", + "tags": [ + "Alert Configurations" + ] + } + }, + "/api/atlas/v2/search/test": { + "get": { + "description": "This resource returns information about the MongoDB application along with API key meta data.", + "operationId": "getSystemStatus", + "parameters": [ + { + "$ref": "#/components/parameters/envelope" + }, + { + "$ref": "#/components/parameters/pretty" + } + ], + "responses": { + "200": { + "content": { + "application/vnd.atlas.2023-01-01+json": { + "schema": { + "$ref": "base.json#/components/schemas/SystemStatus" + }, + "x-xgen-version": "2023-01-01" + } + }, + "description": "OK" + }, + "401": { + "$ref": "base.json#/components/responses/unauthorized" + }, + "404": { + "$ref": "base.json#/components/responses/notFound" + }, + "500": { + "$ref": "base.json#/components/responses/internalServerError" + } + }, + "summary": "Return the status of this MongoDB application", + "tags": [ + "Search Test" + ] + } + } + }, + "servers": [ + { + "url": "https://cloud.mongodb.com" + } + ], + "tags": [ + { + "description": "Returns access logs for authentication attempts made to Atlas database deployments. To view database access history, you must have either the Project Owner or Organization Owner role.", + "name": "Access Tracking" + }, + { + "description": "Returns and edits the conditions that trigger alerts and how MongoDB Cloud notifies users. This collection remains under revision and may change.", + "name": "Alert Configurations" + } + ] +} diff --git a/tools/cli/test/data/base.json b/tools/cli/test/data/base.json new file mode 100644 index 0000000..caf0ab9 --- /dev/null +++ b/tools/cli/test/data/base.json @@ -0,0 +1,441 @@ +{ + "openapi": "3.0.1", + "info": { + "description": "The MongoDB Atlas Administration API allows developers to manage all components in MongoDB Atlas.\n\nThe Atlas Administration API uses HTTP Digest Authentication to authenticate requests. Provide a programmatic API public key and corresponding private key as the username and password when constructing the HTTP request. For example, to [return database access history](#tag/Access-Tracking/operation/listAccessLogsByClusterName) with [cURL](https://en.wikipedia.org/wiki/CURL), run the following command in the terminal:\n\n```\ncurl --user \"{PUBLIC-KEY}:{PRIVATE-KEY}\" \\\n --digest \\\n --header \"Accept: application/vnd.atlas.2023-02-01+json\" \\\n GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/dbAccessHistory/clusters/{clusterName}?pretty=true\"\n```\n\nTo learn more, see [Get Started with the Atlas Administration API](https://www.mongodb.com/docs/atlas/configure-api-access/). For support, see [MongoDB Support](https://www.mongodb.com/support/get-started).", + "license": { + "name": "CC BY-NC-SA 3.0 US", + "url": "https://creativecommons.org/licenses/by-nc-sa/3.0/us/" + }, + "termsOfService": "https://www.mongodb.com/mongodb-management-service-terms-and-conditions", + "title": "MongoDB Atlas Administration API", + "version": "2.0", + "x-xgen-sha": "7bcbc2297ef0216fabc79c752cf21b132fc76577" + }, + "servers": [ + { + "url": "https://cloud.mongodb.com" + } + ], + "tags": [ + { + "description": "Returns access logs for authentication attempts made to Atlas database deployments. To view database access history, you must have either the Project Owner or Organization Owner role.", + "name": "Access Tracking" + }, + { + "description": "Returns and edits the conditions that trigger alerts and how MongoDB Cloud notifies users. This collection remains under revision and may change.", + "name": "Alert Configurations" + } + ], + "paths": { + "/api/atlas/v2": { + "get": { + "description": "This resource returns information about the MongoDB application along with API key meta data.", + "operationId": "getSystemStatus", + "parameters": [ + { + "$ref": "#/components/parameters/envelope" + }, + { + "$ref": "#/components/parameters/pretty" + } + ], + "responses": { + "200": { + "content": { + "application/vnd.atlas.2023-01-01+json": { + "schema": { + "$ref": "#/components/schemas/SystemStatus" + }, + "x-xgen-version": "2023-01-01" + } + }, + "description": "OK" + }, + "401": { + "$ref": "#/components/responses/unauthorized" + }, + "404": { + "$ref": "#/components/responses/notFound" + }, + "500": { + "$ref": "#/components/responses/internalServerError" + } + }, + "summary": "Return the status of this MongoDB application", + "tags": [ + "Root" + ] + } + }, + "/api/atlas/v2/alertConfigs/matchers/fieldNames": { + "get": { + "description": "Get all field names that the `matchers.fieldName` parameter accepts when you create or update an Alert Configuration. You can successfully call this endpoint with any assigned role.", + "operationId": "listAlertConfigurationMatchersFieldNames", + "parameters": [ + { + "$ref": "#/components/parameters/envelope" + }, + { + "$ref": "#/components/parameters/pretty" + } + ], + "responses": { + "200": { + "content": { + "application/vnd.atlas.2023-01-01+json": { + "schema": { + "type": "array", + "items": { + "$ref": "external.json#/components/schemas/ApiAtlasFTSAnalyzersViewManual" + } + }, + "x-xgen-version": "2023-01-01" + } + }, + "description": "OK" + }, + "401": { + "$ref": "#/components/responses/unauthorized" + }, + "500": { + "$ref": "#/components/responses/internalServerError" + } + }, + "security": [ + { + "DigestAuth": [] + } + ], + "summary": "Get All Alert Configuration Matchers Field Names", + "tags": [ + "Alert Configurations" + ] + } + } + }, + "components": { + "schemas": { + "Link": { + "type": "object", + "properties": { + "href": { + "type": "string", + "description": "Uniform Resource Locator (URL) that points another API resource to which this response has some relationship. This URL often begins with `https://cloud.mongodb.com/api/atlas`.", + "example": "https://cloud.mongodb.com/api/atlas" + }, + "rel": { + "type": "string", + "description": "Uniform Resource Locator (URL) that defines the semantic relationship between this resource and another API resource. This URL often begins with `https://cloud.mongodb.com/api/atlas`.", + "example": "self" + } + } + }, + "ApiKey": { + "type": "object", + "description": "Details contained in one API key.", + "nullable": true, + "properties": { + "id": { + "type": "string", + "description": "Unique 24-hexadecimal digit string that identifies this organization API key.", + "example": "32b6e34b3d91647abb20e7b8", + "maxLength": 24, + "minLength": 24, + "pattern": "^([a-f0-9]{24})$", + "readOnly": true + }, + "publicKey": { + "type": "string", + "description": "Public API key value set for the specified organization API key.", + "maxLength": 8, + "minLength": 8, + "readOnly": true + } + }, + "readOnly": true, + "required": [ + "id", + "publicKey" + ] + }, + "SystemStatus": { + "type": "object", + "properties": { + "apiKey": { + "$ref": "#/components/schemas/ApiKey" + }, + "appName": { + "type": "string", + "description": "Human-readable label that identifies the service from which you requested this response.", + "enum": [ + "MongoDB Atlas" + ], + "readOnly": true + }, + "build": { + "type": "string", + "description": "Unique 40-hexadecimal digit hash that identifies the latest git commit merged for this application.", + "example": "83be55e140f493c88e7f578aae96548dd881587b", + "readOnly": true + }, + "links": { + "type": "array", + "description": "List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships.", + "externalDocs": { + "description": "Web Linking Specification (RFC 5988)", + "url": "https://datatracker.ietf.org/doc/html/rfc5988" + }, + "items": { + "$ref": "#/components/schemas/Link" + }, + "readOnly": true + }, + "throttling": { + "type": "boolean", + "description": "Flag that indicates whether someone enabled throttling on this service.", + "readOnly": true + } + }, + "required": [ + "apiKey", + "appName", + "build", + "throttling" + ] + }, + "ApiError": { + "type": "object", + "properties": { + "detail": { + "type": "string", + "description": "Describes the specific conditions or reasons that cause each type of error." + }, + "error": { + "type": "integer", + "format": "int32", + "description": "HTTP status code returned with this error.", + "maximum": 599, + "minimum": 200 + }, + "errorCode": { + "type": "string", + "description": "Application error code returned with this error.", + "example": "TOO_MANY_GROUP_NOTIFICATIONS" + }, + "parameters": { + "type": "array", + "description": "Parameter uses to give more information about the error." + }, + "reason": { + "type": "string", + "description": "Application error message returned with this error.", + "example": "At most one group notification can be specified for an alert configuration." + } + } + } + }, + "parameters": { + "envelope": { + "description": "Flag that indicates whether Application wraps the response in an `envelope` JSON object. Some API clients cannot access the HTTP response headers or status code. To remediate this, set envelope=true in the query. Endpoints that return a list of results use the results object as an envelope. Application adds the status parameter to the response body.", + "in": "query", + "name": "envelope", + "required": false, + "schema": { + "type": "boolean", + "default": false, + "example": false + } + }, + "pretty": { + "description": "Flag that indicates whether the response body should be in the prettyprint format.", + "in": "query", + "name": "pretty", + "required": false, + "schema": { + "type": "boolean", + "default": false, + "example": false + } + } + }, + "responses": { + "accepted": { + "description": "Accepted." + }, + "badRequest": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint) No provider AWS exists.", + "error": 400, + "errorCode": "INVALID_PROVIDER", + "parameters": [ + "AWS" + ], + "reason": "Bad Request" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Bad Request." + }, + "conflict": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint) Cannot delete organization link while there is active migration in following project ids: 60c4fd418ebe251047c50554", + "error": 409, + "errorCode": "CANNOT_DELETE_ORG_LINK_WITH_RUNNING_LIVE_EXPORT", + "parameters": [ + "60c4fd418ebe251047c50554" + ], + "reason": "Conflict" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Conflict." + }, + "forbidden": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint)", + "error": 403, + "errorCode": "CANNOT_CHANGE_GROUP_NAME", + "parameters": [ + "EXAMPLE" + ], + "reason": "Forbidden" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Forbidden." + }, + "gone": { + "content": { + "application/json": { + "example": { + "detail": "This happens when a resource is marked for sunset and the sunset date is in the past.", + "error": 410, + "errorCode": "VERSION_GONE", + "parameters": [ + "EXAMPLE" + ], + "reason": "Gone" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Gone." + }, + "internalServerError": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint)", + "error": 500, + "errorCode": "UNEXPECTED_ERROR", + "parameters": [ + "EXAMPLE" + ], + "reason": "Internal Server Error" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Internal Server Error." + }, + "methodNotAllowed": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint)", + "error": 405, + "errorCode": "ATLAS_BACKUP_CANCEL_SHARD_RESTORE_JOB_NOT_ALLOWED", + "parameters": [ + "EXAMPLE" + ], + "reason": "Method Not Allowed" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Method Not Allowed." + }, + "noBody": { + "description": "This endpoint does not return a response body." + }, + "notFound": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint) Cannot find resource AWS", + "error": 404, + "errorCode": "RESOURCE_NOT_FOUND", + "parameters": [ + "AWS" + ], + "reason": "Not Found" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not Found." + }, + "paymentRequired": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint)", + "error": 402, + "errorCode": "NO_PAYMENT_INFORMATION_FOUND", + "parameters": [ + "EXAMPLE" + ], + "reason": "Payment Required" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Payment Required." + }, + "unauthorized": { + "content": { + "application/json": { + "example": { + "detail": "(This is just an example, the exception may not be related to this endpoint)", + "error": 401, + "errorCode": "NOT_ORG_GROUP_CREATOR", + "parameters": [ + "EXAMPLE" + ], + "reason": "Unauthorized" + }, + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Unauthorized." + } + } + } +} diff --git a/tools/cli/test/data/external.json b/tools/cli/test/data/external.json new file mode 100644 index 0000000..67b7201 --- /dev/null +++ b/tools/cli/test/data/external.json @@ -0,0 +1,899 @@ +{ + "openapi": "3.0.1", + "info": {}, + "paths": { + "/api/atlas/v2/search/test": { + "get": { + "description": "This resource returns information about the MongoDB application along with API key meta data.", + "operationId": "getSystemStatus", + "parameters": [ + { + "$ref": "#/components/parameters/envelope" + }, + { + "$ref": "#/components/parameters/pretty" + } + ], + "responses": { + "200": { + "content": { + "application/vnd.atlas.2023-01-01+json": { + "schema": { + "$ref": "base.json#/components/schemas/SystemStatus" + }, + "x-xgen-version": "2023-01-01" + } + }, + "description": "OK" + }, + "401": { + "$ref": "base.json#/components/responses/unauthorized" + }, + "404": { + "$ref": "base.json#/components/responses/notFound" + }, + "500": { + "$ref": "base.json#/components/responses/internalServerError" + } + }, + "summary": "Return the status of this MongoDB application", + "tags": [ + "Search Test" + ] + } + } + }, + "components": { + "schemas": { + "ApiAtlasFTSAnalyzersViewManual": { + "type": "object", + "title": "analyzers", + "description": "Settings that describe one Atlas Search custom analyzer.", + "required": [ + "name", + "tokenizer" + ], + "properties": { + "name": { + "type": "string", + "description": "Human-readable name that identifies the custom analyzer. Names must be unique within an index, and must not start with any of the following strings:\n- `lucene.`\n- `builtin.`\n- `mongodb.`" + }, + "charFilters": { + "type": "array", + "description": "Filters that examine text one character at a time and perform filtering operations.", + "items": { + "type": "object", + "oneOf": [ + { + "$ref": "#/components/schemas/charFilterhtmlStrip" + }, + { + "$ref": "#/components/schemas/charFiltericuNormalize" + }, + { + "$ref": "#/components/schemas/charFiltermapping" + }, + { + "$ref": "#/components/schemas/charFilterpersian" + } + ] + } + }, + "tokenizer": { + "type": "object", + "description": "Tokenizer that you want to use to create tokens. Tokens determine how Atlas Search splits up text into discrete chunks for indexing.", + "discriminator": { + "mapping": { + "edgeGram": "#/components/schemas/tokenizeredgeGram", + "keyword": "#/components/schemas/tokenizerkeyword", + "nGram": "#/components/schemas/tokenizernGram", + "regexCaptureGroup": "#/components/schemas/tokenizerregexCaptureGroup", + "regexSplit": "#/components/schemas/tokenizerregexSplit", + "standard": "#/components/schemas/tokenizerstandard", + "uaxUrlEmail": "#/components/schemas/tokenizeruaxUrlEmail", + "whitespace": "#/components/schemas/tokenizerwhitespace" + }, + "propertyName": "type" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/tokenizeredgeGram" + }, + { + "$ref": "#/components/schemas/tokenizerkeyword" + }, + { + "$ref": "#/components/schemas/tokenizernGram" + }, + { + "$ref": "#/components/schemas/tokenizerregexCaptureGroup" + }, + { + "$ref": "#/components/schemas/tokenizerregexSplit" + }, + { + "$ref": "#/components/schemas/tokenizerstandard" + }, + { + "$ref": "#/components/schemas/tokenizeruaxUrlEmail" + }, + { + "$ref": "#/components/schemas/tokenizerwhitespace" + } + ] + }, + "tokenFilters": { + "type": "array", + "description": "Filter that performs operations such as:\n\n- Stemming, which reduces related words, such as \"talking\", \"talked\", and \"talks\" to their root word \"talk\".\n\n- Redaction, the removal of sensitive information from public documents.", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/tokenFilterasciiFolding" + }, + { + "$ref": "#/components/schemas/tokenFilterdaitchMokotoffSoundex" + }, + { + "$ref": "#/components/schemas/tokenFilteredgeGram" + }, + { + "$ref": "#/components/schemas/tokenFiltericuFolding" + }, + { + "$ref": "#/components/schemas/tokenFiltericuNormalizer" + }, + { + "$ref": "#/components/schemas/tokenFilterlength" + }, + { + "$ref": "#/components/schemas/tokenFilterlowercase" + }, + { + "$ref": "#/components/schemas/tokenFilternGram" + }, + { + "$ref": "#/components/schemas/tokenFilterregex" + }, + { + "$ref": "#/components/schemas/tokenFilterreverse" + }, + { + "$ref": "#/components/schemas/tokenFiltershingle" + }, + { + "$ref": "#/components/schemas/tokenFiltersnowballStemming" + }, + { + "$ref": "#/components/schemas/tokenFilterstopword" + }, + { + "$ref": "#/components/schemas/tokenFiltertrim" + } + ] + } + } + } + }, + "charFilterhtmlStrip": { + "title": "htmlStrip", + "type": "object", + "required": [ + "type" + ], + "description": "Filter that strips out HTML constructs.", + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this character filter type.", + "enum": [ + "htmlStrip" + ] + }, + "ignoredTags": { + "type": "array", + "description": "The HTML tags that you want to exclude from filtering.", + "items": { + "type": "string" + } + } + } + }, + "charFiltericuNormalize": { + "title": "icuNormalize", + "type": "object", + "description": "Filter that processes normalized text with the ICU Normalizer. It is based on Lucene's ICUNormalizer2CharFilter.", + "ExternalDocs": { + "description": "ICUNormalizer2CharFilter", + "url": "https://lucene.apache.org/core/8_3_0/analyzers-icu/org/apache/lucene/analysis/icu/ICUNormalizer2CharFilter.html" + }, + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this character filter type.", + "enum": [ + "icuNormalize" + ] + } + } + }, + "charFiltermapping": { + "title": "mapping", + "type": "object", + "description": "Filter that applies normalization mappings that you specify to characters.", + "required": [ + "type", + "mappings" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this character filter type.", + "enum": [ + "mapping" + ] + }, + "mappings": { + "type": "object", + "description": "Comma-separated list of mappings. A mapping indicates that one character or group of characters should be substituted for another, using the following format:\n\n` : `", + "properties": { + "additionalProperties": { + "type": "string" + } + } + } + } + }, + "charFilterpersian": { + "title": "persian", + "type": "object", + "required": [ + "type" + ], + "description": "Filter that replaces instances of a zero-width non-joiner with an ordinary space. It is based on Lucene's PersianCharFilter.", + "externalDocs": { + "description": "PersianCharFilter", + "url": "https://lucene.apache.org/core/8_0_0/analyzers-common/org/apache/lucene/analysis/fa/PersianCharFilter.html" + }, + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this character filter type.", + "enum": [ + "persian" + ] + } + } + }, + "tokenizernGram": { + "title": "nGram", + "type": "object", + "description": "Tokenizer that splits input into text chunks, or \"n-grams\", of into given sizes. You can't use the nGram tokenizer in synonym or autocomplete mapping definitions.", + "required": [ + "type", + "minGram", + "maxGram" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this tokenizer type.", + "enum": [ + "edgeGram" + ] + }, + "minGram": { + "type": "integer", + "description": "Characters to include in the shortest token that Atlas Search creates." + }, + "maxGram": { + "type": "integer", + "description": "Characters to include in the longest token that Atlas Search creates." + } + } + }, + "tokenizeredgeGram": { + "title": "edgeGram", + "type": "object", + "description": "Tokenizer that splits input from the left side, or \"edge\", of a text input into n-grams of given sizes. You can't use the edgeGram tokenizer in synonym or autocomplete mapping definitions.", + "required": [ + "type", + "minGram", + "maxGram" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this tokenizer type.", + "enum": [ + "edgeGram" + ] + }, + "minGram": { + "type": "integer", + "description": "Characters to include in the shortest token that Atlas Search creates." + }, + "maxGram": { + "type": "integer", + "description": "Characters to include in the longest token that Atlas Search creates." + } + } + }, + "tokenizerkeyword": { + "title": "keyword", + "type": "object", + "description": "Tokenizer that combines the entire input as a single token.", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this tokenizer type.", + "enum": [ + "keyword" + ] + } + } + }, + "tokenizerregexCaptureGroup": { + "title": "regexCaptureGroup", + "type": "object", + "description": "Tokenizer that uses a regular expression pattern to extract tokens.", + "required": [ + "type", + "pattern", + "group" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this tokenizer type.", + "enum": [ + "regexCaptureGroup" + ] + }, + "pattern": { + "type": "string", + "description": "Regular expression to match against." + }, + "group": { + "type": "integer", + "description": "Index of the character group within the matching expression to extract into tokens. Use `0` to extract all character groups." + } + } + }, + "tokenizerregexSplit": { + "title": "regexSplit", + "type": "object", + "description": "Tokenizer that splits tokens using a regular-expression based delimiter.", + "required": [ + "type", + "pattern" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this tokenizer type.", + "enum": [ + "regexSplit" + ] + }, + "pattern": { + "type": "string", + "description": "Regular expression to match against." + } + } + }, + "tokenizerstandard": { + "title": "standard", + "type": "object", + "description": "Tokenizer that splits tokens based on word break rules from the Unicode Text Segmentation algorithm.", + "externalDocs": { + "description": "Unicode Text Segmentation Algorithm", + "url": "https://www.unicode.org/L2/L2019/19034-uax29-34-draft.pdf" + }, + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this tokenizer type.", + "enum": [ + "standard" + ] + }, + "maxTokenLength": { + "type": "integer", + "description": "Maximum number of characters in a single token. Tokens greater than this length are split at this length into multiple tokens.", + "default": 255 + } + } + }, + "tokenizeruaxUrlEmail": { + "title": "uaxUrlEmail", + "type": "object", + "description": "Tokenizer that creates tokens from URLs and email addresses. Although this tokenizer uses word break rules from the Unicode Text Segmentation algorithm, we recommend using it only when the indexed field value includes URLs and email addresses. For fields that don't include URLs or email addresses, use the **standard** tokenizer to create tokens based on word break rules.", + "externalDocs": { + "description": "Unicode Text Segmentation Algorithm", + "url": "https://www.unicode.org/L2/L2019/19034-uax29-34-draft.pdf" + }, + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this tokenizer type.", + "enum": [ + "uaxUrlEmail" + ] + }, + "maxTokenLength": { + "type": "integer", + "description": "Maximum number of characters in a single token. Tokens greater than this length are split at this length into multiple tokens.", + "default": 255 + } + } + }, + "tokenizerwhitespace": { + "title": "whitespace", + "type": "object", + "description": "Tokenizer that creates tokens based on occurrences of whitespace between words.", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this tokenizer type.", + "enum": [ + "whitespace" + ] + }, + "maxTokenLength": { + "type": "integer", + "description": "Maximum number of characters in a single token. Tokens greater than this length are split at this length into multiple tokens.", + "default": 255 + } + } + }, + "tokenFilterasciiFolding": { + "title": "asciiFolding", + "type": "object", + "description": "Filter that converts alphabetic, numeric, and symbolic unicode characters that are not in the Basic Latin Unicode block to their ASCII equivalents, if available.", + "externalDocs": { + "description": "Basic Latin Unicode block", + "url": "https://en.wikipedia.org/wiki/Basic_Latin_(Unicode_block)" + }, + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this token filter type.", + "enum": [ + "asciiFolding" + ] + }, + "originalTokens": { + "type": "string", + "description": "Value that indicates whether to include or omit the original tokens in the output of the token filter.\n\nChoose `include` if you want to support queries on both the original tokens as well as the converted forms.\n\n Choose `omit` if you want to query only on the converted forms of the original tokens.", + "enum": [ + "omit", + "include" + ], + "default": "omit" + } + } + }, + "tokenFilterdaitchMokotoffSoundex": { + "title": "daitchMokotoffSoundex", + "type": "object", + "description": "Filter that creates tokens for words that sound the same based on the Daitch-Mokotoff Soundex phonetic algorithm. This filter can generate multiple encodings for each input, where each encoded token is a 6 digit number.\n\n**NOTE**: Don't use the **daitchMokotoffSoundex** token filter in:\n\n-Synonym or autocomplete mapping definitions\n- Operators where **fuzzy** is enabled. Atlas Search supports the **fuzzy** option only for the **autocomplete**, **term**, and **text** operators.", + "externalDocs": { + "description": "Daitch-Mokotoff Soundex phonetic algorithm", + "url": "https://en.wikipedia.org/wiki/Daitch%E2%80%93Mokotoff_Soundex" + }, + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this token filter type.", + "enum": [ + "daitchMokotoffSoundex" + ] + }, + "originalTokens": { + "type": "string", + "description": "Value that indicates whether to include or omit the original tokens in the output of the token filter.\n\nChoose `include` if you want to support queries on both the original tokens as well as the converted forms.\n\n Choose `omit` if you want to query only on the converted forms of the original tokens.", + "enum": [ + "omit", + "include" + ], + "default": "include" + } + } + }, + "tokenFilteredgeGram": { + "title": "edgeGram", + "type": "object", + "description": "Filter that tokenizes input from the left side, or \"edge\", of a text input into n-grams of configured sizes. You can't use this token filter in synonym or autocomplete mapping definitions.", + "required": [ + "type", + "minGram", + "maxGram" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this token filter type.", + "enum": [ + "edgeGram" + ] + }, + "minGram": { + "type": "integer", + "description": "Value that specifies the minimum length of generated n-grams. This value must be less than or equal to **maxGram**." + }, + "maxGram": { + "type": "integer", + "description": "Value that specifies the maximum length of generated n-grams. This value must be greater than or equal to **minGram**." + }, + "termNotInBounds": { + "type": "string", + "description": "Value that indicates whether to index tokens shorter than **minGram** or longer than **maxGram**.", + "enum": [ + "omit", + "include" + ], + "default": "omit" + } + } + }, + "tokenFiltericuFolding": { + "title": "icuFolding", + "type": "object", + "description": "Filter that applies character folding from Unicode Technical Report #30.", + "externalDocs": { + "description": "Unicode Technical Report #30", + "url": "http://www.unicode.org/reports/tr30/tr30-4.html" + }, + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this token filter type.", + "enum": [ + "icuFolding" + ] + } + } + }, + "tokenFiltericuNormalizer": { + "title": "icuNormalizer", + "type": "object", + "description": "Filter that normalizes tokens using a standard Unicode Normalization Mode.", + "externalDocs": { + "description": "Unicode Normalization Mode", + "url": "https://unicode.org/reports/tr15/" + }, + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this token filter type.", + "enum": [ + "icuNormalizer" + ] + }, + "normalizationForm": { + "type": "string", + "description": "Normalization form to apply.", + "enum": [ + "nfd", + "nfc", + "nfkd", + "nfkc" + ], + "default": "nfc" + } + } + }, + "tokenFilterlength": { + "title": "length", + "type": "object", + "description": "Filter that removes tokens that are too short or too long.", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this token filter type.", + "enum": [ + "length" + ] + }, + "min": { + "type": "integer", + "description": "Number that specifies the minimum length of a token. This value must be less than or equal to **max**.", + "default": 0 + }, + "max": { + "type": "integer", + "description": "Number that specifies the maximum length of a token. Value must be greater than or equal to **min**.", + "default": 255 + } + } + }, + "tokenFilterlowercase": { + "title": "lowercase", + "type": "object", + "description": "Filter that normalizes token text to lowercase.", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this token filter type.", + "enum": [ + "lowercase" + ] + } + } + }, + "tokenFilternGram": { + "title": "nGram", + "type": "object", + "description": "Filter that tokenizes input into n-grams of configured sizes. You can't use this token filter in synonym or autocomplete mapping definitions.", + "required": [ + "type", + "minGram", + "maxGram" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this token filter type.", + "enum": [ + "nGram" + ] + }, + "minGram": { + "type": "integer", + "description": "Value that specifies the minimum length of generated n-grams. This value must be less than or equal to **maxGram**." + }, + "maxGram": { + "type": "integer", + "description": "Value that specifies the maximum length of generated n-grams. This value must be greater than or equal to **minGram**." + }, + "termNotInBounds": { + "type": "string", + "description": "Value that indicates whether to index tokens shorter than **minGram** or longer than **maxGram**.", + "enum": [ + "omit", + "include" + ], + "default": "omit" + } + } + }, + "tokenFilterregex": { + "title": "regex", + "type": "object", + "description": "Filter that applies a regular expression to each token, replacing matches with a specified string.", + "required": [ + "type", + "pattern", + "replacement", + "matches" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this token filter type.", + "enum": [ + "regex" + ] + }, + "pattern": { + "type": "string", + "description": "Regular expression pattern to apply to each token." + }, + "replacement": { + "type": "string", + "description": "Replacement string to substitute wherever a matching pattern occurs." + }, + "matches": { + "type": "string", + "description": "Value that indicates whether to replace only the first matching pattern or all matching patterns.", + "enum": [ + "all", + "first" + ] + } + } + }, + "tokenFilterreverse": { + "title": "reverse", + "type": "object", + "description": "Filter that reverses each string token.", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this token filter type.", + "enum": [ + "reverse" + ] + } + } + }, + "tokenFiltershingle": { + "title": "shingle", + "type": "object", + "description": "Filter that constructs shingles (token n-grams) from a series of tokens. You can't use this token filter in synonym or autocomplete mapping definitions.", + "required": [ + "type", + "minShingleSize", + "maxShingleSize" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this token filter type.", + "enum": [ + "shingle" + ] + }, + "minShingleSize": { + "type": "integer", + "description": "Value that specifies the minimum number of tokens per shingle. This value must be less than or equal to **maxShingleSize**." + }, + "maxShingleSize": { + "type": "integer", + "description": "Value that specifies the maximum number of tokens per shingle. This value must be greater than or equal to **minShingleSize**." + } + } + }, + "tokenFiltersnowballStemming": { + "title": "snowballStemming", + "type": "object", + "description": "Filter that stems tokens using a Snowball-generated stemmer.", + "externalDocs": { + "description": "Snowball-generated stemmer", + "url": "https://snowballstem.org/" + }, + "required": [ + "type", + "stemmerName" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this token filter type.", + "enum": [ + "snowballStemming" + ] + }, + "stemmerName": { + "type": "string", + "description": "Snowball-generated stemmer to use.", + "enum": [ + "arabic", + "armenian", + "basque", + "catalan", + "danish", + "dutch", + "english", + "finnish", + "french", + "german", + "german2", + "hungarian", + "irish", + "italian", + "kp", + "lithuanian", + "lovins", + "norwegian", + "porter", + "portuguese", + "romanian", + "russian", + "spanish", + "swedish", + "turkish" + ] + } + } + }, + "tokenFilterstopword": { + "title": "stopword", + "type": "object", + "description": "Filter that removes tokens that correspond to the specified stop words. This token filter doesn't analyze the stop words that you specify.", + "required": [ + "type", + "tokens" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this token filter type.", + "enum": [ + "stopword" + ] + }, + "tokens": { + "type": "array", + "description": "The stop words that correspond to the tokens to remove. Value must be one or more stop words.", + "items": { + "type": "string" + } + }, + "ignoreCase": { + "type": "boolean", + "description": "Flag that indicates whether to ignore the case of stop words when filtering the tokens to remove.", + "default": true + } + } + }, + "tokenFiltertrim": { + "title": "trim", + "type": "object", + "description": "Filter that trims leading and trailing whitespace from tokens.", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "description": "Human-readable label that identifies this token filter type.", + "enum": [ + "trim" + ] + } + } + } + }, + "parameters": { + "envelope": { + "description": "Flag that indicates whether Application wraps the response in an `envelope` JSON object. Some API clients cannot access the HTTP response headers or status code. To remediate this, set envelope=true in the query. Endpoints that return a list of results use the results object as an envelope. Application adds the status parameter to the response body.", + "in": "query", + "name": "envelope", + "required": false, + "schema": { + "type": "boolean", + "default": false, + "example": false + } + }, + "pretty": { + "description": "Flag that indicates whether the response body should be in the prettyprint format.", + "in": "query", + "name": "pretty", + "required": false, + "schema": { + "type": "boolean", + "default": false, + "example": false + } + } + } + } +} From d0a3cb4cb401d3e570f1b875d0f4442348e67428 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 8 Mar 2024 17:55:23 +0000 Subject: [PATCH 7/9] Update Makefile --- tools/cli/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/cli/Makefile b/tools/cli/Makefile index 044821a..8dc6f04 100644 --- a/tools/cli/Makefile +++ b/tools/cli/Makefile @@ -24,7 +24,6 @@ deps: ## Download go module dependencies .PHONY: devtools devtools: ## Install dev tools @echo "==> Installing dev tools..." - go install go.uber.org/mock/mockgen@latest curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin $(GOLANGCI_VERSION) .PHONY: setup From 895dd2ef7721e6a829d30cc005898459b794ac4a Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Fri, 8 Mar 2024 17:55:43 +0000 Subject: [PATCH 8/9] Delete FOAS.json --- tools/cli/FOAS.json | 434 -------------------------------------------- 1 file changed, 434 deletions(-) delete mode 100644 tools/cli/FOAS.json diff --git a/tools/cli/FOAS.json b/tools/cli/FOAS.json deleted file mode 100644 index 7527ffc..0000000 --- a/tools/cli/FOAS.json +++ /dev/null @@ -1,434 +0,0 @@ -{ - "components": { - "parameters": { - "envelope": { - "description": "Flag that indicates whether Application wraps the response in an `envelope` JSON object. Some API clients cannot access the HTTP response headers or status code. To remediate this, set envelope=true in the query. Endpoints that return a list of results use the results object as an envelope. Application adds the status parameter to the response body.", - "in": "query", - "name": "envelope", - "schema": { - "default": false, - "example": false, - "type": "boolean" - } - }, - "pretty": { - "description": "Flag that indicates whether the response body should be in the prettyprint format.", - "in": "query", - "name": "pretty", - "schema": { - "default": false, - "example": false, - "type": "boolean" - } - } - }, - "responses": { - "accepted": { - "description": "Accepted." - }, - "badRequest": { - "content": { - "application/json": { - "example": { - "detail": "(This is just an example, the exception may not be related to this endpoint) No provider AWS exists.", - "error": 400, - "errorCode": "INVALID_PROVIDER", - "parameters": [ - "AWS" - ], - "reason": "Bad Request" - }, - "schema": { - "$ref": "#/components/schemas/ApiError" - } - } - }, - "description": "Bad Request." - }, - "conflict": { - "content": { - "application/json": { - "example": { - "detail": "(This is just an example, the exception may not be related to this endpoint) Cannot delete organization link while there is active migration in following project ids: 60c4fd418ebe251047c50554", - "error": 409, - "errorCode": "CANNOT_DELETE_ORG_LINK_WITH_RUNNING_LIVE_EXPORT", - "parameters": [ - "60c4fd418ebe251047c50554" - ], - "reason": "Conflict" - }, - "schema": { - "$ref": "#/components/schemas/ApiError" - } - } - }, - "description": "Conflict." - }, - "forbidden": { - "content": { - "application/json": { - "example": { - "detail": "(This is just an example, the exception may not be related to this endpoint)", - "error": 403, - "errorCode": "CANNOT_CHANGE_GROUP_NAME", - "parameters": [ - "EXAMPLE" - ], - "reason": "Forbidden" - }, - "schema": { - "$ref": "#/components/schemas/ApiError" - } - } - }, - "description": "Forbidden." - }, - "gone": { - "content": { - "application/json": { - "example": { - "detail": "This happens when a resource is marked for sunset and the sunset date is in the past.", - "error": 410, - "errorCode": "VERSION_GONE", - "parameters": [ - "EXAMPLE" - ], - "reason": "Gone" - }, - "schema": { - "$ref": "#/components/schemas/ApiError" - } - } - }, - "description": "Gone." - }, - "internalServerError": { - "content": { - "application/json": { - "example": { - "detail": "(This is just an example, the exception may not be related to this endpoint)", - "error": 500, - "errorCode": "UNEXPECTED_ERROR", - "parameters": [ - "EXAMPLE" - ], - "reason": "Internal Server Error" - }, - "schema": { - "$ref": "#/components/schemas/ApiError" - } - } - }, - "description": "Internal Server Error." - }, - "methodNotAllowed": { - "content": { - "application/json": { - "example": { - "detail": "(This is just an example, the exception may not be related to this endpoint)", - "error": 405, - "errorCode": "ATLAS_BACKUP_CANCEL_SHARD_RESTORE_JOB_NOT_ALLOWED", - "parameters": [ - "EXAMPLE" - ], - "reason": "Method Not Allowed" - }, - "schema": { - "$ref": "#/components/schemas/ApiError" - } - } - }, - "description": "Method Not Allowed." - }, - "noBody": { - "description": "This endpoint does not return a response body." - }, - "notFound": { - "content": { - "application/json": { - "example": { - "detail": "(This is just an example, the exception may not be related to this endpoint) Cannot find resource AWS", - "error": 404, - "errorCode": "RESOURCE_NOT_FOUND", - "parameters": [ - "AWS" - ], - "reason": "Not Found" - }, - "schema": { - "$ref": "#/components/schemas/ApiError" - } - } - }, - "description": "Not Found." - }, - "paymentRequired": { - "content": { - "application/json": { - "example": { - "detail": "(This is just an example, the exception may not be related to this endpoint)", - "error": 402, - "errorCode": "NO_PAYMENT_INFORMATION_FOUND", - "parameters": [ - "EXAMPLE" - ], - "reason": "Payment Required" - }, - "schema": { - "$ref": "#/components/schemas/ApiError" - } - } - }, - "description": "Payment Required." - }, - "unauthorized": { - "content": { - "application/json": { - "example": { - "detail": "(This is just an example, the exception may not be related to this endpoint)", - "error": 401, - "errorCode": "NOT_ORG_GROUP_CREATOR", - "parameters": [ - "EXAMPLE" - ], - "reason": "Unauthorized" - }, - "schema": { - "$ref": "#/components/schemas/ApiError" - } - } - }, - "description": "Unauthorized." - } - }, - "schemas": { - "ApiError": { - "properties": { - "detail": { - "description": "Describes the specific conditions or reasons that cause each type of error.", - "type": "string" - }, - "error": { - "description": "HTTP status code returned with this error.", - "format": "int32", - "maximum": 599, - "minimum": 200, - "type": "integer" - }, - "errorCode": { - "description": "Application error code returned with this error.", - "example": "TOO_MANY_GROUP_NOTIFICATIONS", - "type": "string" - }, - "parameters": { - "description": "Parameter uses to give more information about the error.", - "type": "array" - }, - "reason": { - "description": "Application error message returned with this error.", - "example": "At most one group notification can be specified for an alert configuration.", - "type": "string" - } - }, - "type": "object" - }, - "ApiKey": { - "description": "Details contained in one API key.", - "nullable": true, - "properties": { - "id": { - "description": "Unique 24-hexadecimal digit string that identifies this organization API key.", - "example": "32b6e34b3d91647abb20e7b8", - "maxLength": 24, - "minLength": 24, - "pattern": "^([a-f0-9]{24})$", - "readOnly": true, - "type": "string" - }, - "publicKey": { - "description": "Public API key value set for the specified organization API key.", - "maxLength": 8, - "minLength": 8, - "readOnly": true, - "type": "string" - } - }, - "readOnly": true, - "required": [ - "id", - "publicKey" - ], - "type": "object" - }, - "Link": { - "properties": { - "href": { - "description": "Uniform Resource Locator (URL) that points another API resource to which this response has some relationship. This URL often begins with `https://cloud.mongodb.com/api/atlas`.", - "example": "https://cloud.mongodb.com/api/atlas", - "type": "string" - }, - "rel": { - "description": "Uniform Resource Locator (URL) that defines the semantic relationship between this resource and another API resource. This URL often begins with `https://cloud.mongodb.com/api/atlas`.", - "example": "self", - "type": "string" - } - }, - "type": "object" - }, - "SystemStatus": { - "properties": { - "apiKey": { - "$ref": "#/components/schemas/ApiKey" - }, - "appName": { - "description": "Human-readable label that identifies the service from which you requested this response.", - "enum": [ - "MongoDB Atlas" - ], - "readOnly": true, - "type": "string" - }, - "build": { - "description": "Unique 40-hexadecimal digit hash that identifies the latest git commit merged for this application.", - "example": "83be55e140f493c88e7f578aae96548dd881587b", - "readOnly": true, - "type": "string" - }, - "links": { - "description": "List of one or more Uniform Resource Locators (URLs) that point to API sub-resources, related API resources, or both. RFC 5988 outlines these relationships.", - "externalDocs": { - "description": "Web Linking Specification (RFC 5988)", - "url": "https://datatracker.ietf.org/doc/html/rfc5988" - }, - "items": { - "$ref": "#/components/schemas/Link" - }, - "readOnly": true, - "type": "array" - }, - "throttling": { - "description": "Flag that indicates whether someone enabled throttling on this service.", - "readOnly": true, - "type": "boolean" - } - }, - "required": [ - "apiKey", - "appName", - "build", - "throttling" - ], - "type": "object" - } - } - }, - "info": { - "description": "The MongoDB Atlas Administration API allows developers to manage all components in MongoDB Atlas.\n\nThe Atlas Administration API uses HTTP Digest Authentication to authenticate requests. Provide a programmatic API public key and corresponding private key as the username and password when constructing the HTTP request. For example, to [return database access history](#tag/Access-Tracking/operation/listAccessLogsByClusterName) with [cURL](https://en.wikipedia.org/wiki/CURL), run the following command in the terminal:\n\n```\ncurl --user \"{PUBLIC-KEY}:{PRIVATE-KEY}\" \\\n --digest \\\n --header \"Accept: application/vnd.atlas.2023-02-01+json\" \\\n GET \"https://cloud.mongodb.com/api/atlas/v2/groups/{groupId}/dbAccessHistory/clusters/{clusterName}?pretty=true\"\n```\n\nTo learn more, see [Get Started with the Atlas Administration API](https://www.mongodb.com/docs/atlas/configure-api-access/). For support, see [MongoDB Support](https://www.mongodb.com/support/get-started).", - "license": { - "name": "CC BY-NC-SA 3.0 US", - "url": "https://creativecommons.org/licenses/by-nc-sa/3.0/us/" - }, - "termsOfService": "https://www.mongodb.com/mongodb-management-service-terms-and-conditions", - "title": "MongoDB Atlas Administration API", - "version": "2.0", - "x-xgen-sha": "7bcbc2297ef0216fabc79c752cf21b132fc76577" - }, - "openapi": "3.0.1", - "paths": { - "/api/atlas/v2": { - "get": { - "description": "This resource returns information about the MongoDB application along with API key meta data.", - "operationId": "getSystemStatus", - "parameters": [ - { - "$ref": "#/components/parameters/envelope" - }, - { - "$ref": "#/components/parameters/pretty" - } - ], - "responses": { - "200": { - "content": { - "application/vnd.atlas.2023-01-01+json": { - "schema": { - "$ref": "#/components/schemas/SystemStatus" - }, - "x-xgen-version": "2023-01-01" - } - }, - "description": "OK" - }, - "401": { - "$ref": "#/components/responses/unauthorized" - }, - "404": { - "$ref": "#/components/responses/notFound" - }, - "500": { - "$ref": "#/components/responses/internalServerError" - } - }, - "summary": "Return the status of this MongoDB application", - "tags": [ - "Root" - ] - } - }, - "/api/atlas/v2/search/test": { - "get": { - "description": "This resource returns information about the MongoDB application along with API key meta data.", - "operationId": "getSystemStatus", - "parameters": [ - { - "$ref": "#/components/parameters/envelope" - }, - { - "$ref": "#/components/parameters/pretty" - } - ], - "responses": { - "200": { - "content": { - "application/vnd.atlas.2023-01-01+json": { - "schema": { - "$ref": "base.json#/components/schemas/SystemStatus" - }, - "x-xgen-version": "2023-01-01" - } - }, - "description": "OK" - }, - "401": { - "$ref": "base.json#/components/responses/unauthorized" - }, - "404": { - "$ref": "base.json#/components/responses/notFound" - }, - "500": { - "$ref": "base.json#/components/responses/internalServerError" - } - }, - "summary": "Return the status of this MongoDB application", - "tags": [ - "Search Test" - ] - } - } - }, - "servers": [ - { - "url": "https://cloud.mongodb.com" - } - ], - "tags": [ - { - "description": "Returns access logs for authentication attempts made to Atlas database deployments. To view database access history, you must have either the Project Owner or Organization Owner role.", - "name": "Access Tracking" - }, - { - "description": "Returns and edits the conditions that trigger alerts and how MongoDB Cloud notifies users. This collection remains under revision and may change.", - "name": "Alert Configurations" - } - ] -} From 44225d48bb68c19e5ebca4f50551301157be9482 Mon Sep 17 00:00:00 2001 From: andreaangiolillo Date: Mon, 11 Mar 2024 09:42:59 +0000 Subject: [PATCH 9/9] Update path_conflict_error.go --- .../errors/{pathConflictError.go => path_conflict_error.go} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tools/cli/internal/openapi/errors/{pathConflictError.go => path_conflict_error.go} (67%) diff --git a/tools/cli/internal/openapi/errors/pathConflictError.go b/tools/cli/internal/openapi/errors/path_conflict_error.go similarity index 67% rename from tools/cli/internal/openapi/errors/pathConflictError.go rename to tools/cli/internal/openapi/errors/path_conflict_error.go index 59fd5b8..b01db5a 100644 --- a/tools/cli/internal/openapi/errors/pathConflictError.go +++ b/tools/cli/internal/openapi/errors/path_conflict_error.go @@ -7,5 +7,5 @@ type PathConflictError struct { } func (e PathConflictError) Error() string { - return fmt.Sprintf("there was a conflict with the Path: %s", e.Entry) + return fmt.Sprintf("there was a conflict with the path: %q", e.Entry) }