From 3ed479cd6a1a2c7c562109dc96e2ed44324e2524 Mon Sep 17 00:00:00 2001 From: Andrea Angiolillo Date: Mon, 11 Mar 2024 10:34:26 +0000 Subject: [PATCH] CLOUDP-236398: Add validation logic for paths (#7) --- tools/cli/internal/cli/merge/merge.go | 19 +- .../openapi/errors/path_conflict_error.go | 11 + tools/cli/internal/openapi/oasdiff.go | 78 ++ tools/cli/internal/openapi/openapi.go | 13 + tools/cli/internal/openapi/openapi3.go | 31 + tools/cli/test/data/FOAS.json | 479 ++++++++++ tools/cli/test/data/base.json | 441 +++++++++ tools/cli/test/data/external.json | 899 ++++++++++++++++++ 8 files changed, 1968 insertions(+), 3 deletions(-) create mode 100644 tools/cli/internal/openapi/errors/path_conflict_error.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 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/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/errors/path_conflict_error.go b/tools/cli/internal/openapi/errors/path_conflict_error.go new file mode 100644 index 0000000..b01db5a --- /dev/null +++ b/tools/cli/internal/openapi/errors/path_conflict_error.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: %q", e.Entry) +} diff --git a/tools/cli/internal/openapi/oasdiff.go b/tools/cli/internal/openapi/oasdiff.go new file mode 100644 index 0000000..eec993b --- /dev/null +++ b/tools/cli/internal/openapi/oasdiff.go @@ -0,0 +1,78 @@ +package openapi + +import ( + "log" + "mongodb/openapi/tools/cli/internal/openapi/errors" + + "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 string) (*OasDiff, error) { + parser := NewOpenAPI3() + baseSpec, err := parser.CreateOpenAPISpecFromPath(base) + if err != nil { + return nil, err + } + + return &OasDiff{ + base: baseSpec, + parser: parser, + config: &diff.Config{ + IncludePathParams: true, + }, + }, nil +} + +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 + } + + 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) + 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/openapi.go b/tools/cli/internal/openapi/openapi.go new file mode 100644 index 0000000..5bdb959 --- /dev/null +++ b/tools/cli/internal/openapi/openapi.go @@ -0,0 +1,13 @@ +package openapi + +import ( + "github.com/tufin/oasdiff/load" +) + +type Merger interface { + MergeOpenAPISpecs([]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..014d6bc --- /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) { + 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 + } + + return spec, nil +} 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 + } + } + } + } +}