From 98a0fc5be485d71f72272486923f6206119c3e17 Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Mon, 30 Aug 2021 12:43:51 -0500 Subject: [PATCH 1/2] Add body fields and named body to document APIs --- elasticsearch/_async/client/__init__.py | 200 ++++++++++++------ elasticsearch/_async/client/__init__.pyi | 162 +++++++------- elasticsearch/_async/client/transform.py | 14 +- elasticsearch/_async/client/transform.pyi | 3 +- elasticsearch/client/__init__.py | 200 ++++++++++++------ elasticsearch/client/__init__.pyi | 162 +++++++------- elasticsearch/client/transform.py | 14 +- elasticsearch/client/transform.pyi | 3 +- elasticsearch/client/utils.py | 50 ++++- elasticsearch/client/utils.pyi | 4 +- test_elasticsearch/test_client/test_utils.py | 73 ++++++- .../test_server/test_rest_api_spec.py | 5 +- 12 files changed, 583 insertions(+), 307 deletions(-) diff --git a/elasticsearch/_async/client/__init__.py b/elasticsearch/_async/client/__init__.py index 4eafa82a3..0497607c2 100644 --- a/elasticsearch/_async/client/__init__.py +++ b/elasticsearch/_async/client/__init__.py @@ -300,6 +300,7 @@ async def info(self, params=None, headers=None): "version", "version_type", "wait_for_active_shards", + body_name="document", ) async def create(self, index, id, body, doc_type=None, params=None, headers=None): """ @@ -310,7 +311,7 @@ async def create(self, index, id, body, doc_type=None, params=None, headers=None :arg index: The name of the index :arg id: Document ID - :arg body: The document + :arg document: The document :arg doc_type: The type of the document :arg pipeline: The pipeline id to preprocess incoming documents with @@ -354,6 +355,7 @@ async def create(self, index, id, body, doc_type=None, params=None, headers=None "version", "version_type", "wait_for_active_shards", + body_name="document", ) async def index( self, index, body, doc_type=None, id=None, params=None, headers=None @@ -364,7 +366,7 @@ async def index( ``_ :arg index: The name of the index - :arg body: The document + :arg document: The document :arg doc_type: The type of the document :arg id: Document ID :arg if_primary_term: only perform the index operation if the @@ -996,28 +998,31 @@ async def get(self, index, id, doc_type=None, params=None, headers=None): ``_ - :arg index: The name of the index - :arg id: The document ID + :arg index: Name of the index that contains the document. + :arg id: Unique identifier of the document. :arg doc_type: The type of the document (use `_all` to fetch the first document matching the ID across all types) :arg _source: True or false to return the _source field or not, - or a list of fields to return - :arg _source_excludes: A list of fields to exclude from the - returned _source field - :arg _source_includes: A list of fields to extract and return - from the _source field - :arg preference: Specify the node or shard the operation should - be performed on (default: random) - :arg realtime: Specify whether to perform the operation in - realtime or search mode - :arg refresh: Refresh the shard containing the document before - performing the operation - :arg routing: Specific routing value + or a list of fields to return. + :arg _source_excludes: A comma-separated list of source fields + to exclude in the response. + :arg _source_includes: A comma-separated list of source fields + to include in the response. + :arg preference: Specifies the node or shard the operation + should be performed on. Random by default. + :arg realtime: Boolean) If true, the request is real-time as + opposed to near-real-time. Default: True + :arg refresh: If true, Elasticsearch refreshes the affected + shards to make this operation visible to search. If false, do nothing + with refreshes. + :arg routing: Target the specified primary shard. :arg stored_fields: A comma-separated list of stored fields to return in the response - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type Valid choices: - internal, external, external_gte, force + :arg version: Explicit version number for concurrency control. + The specified version must match the current version of the document for + the request to succeed. + :arg version_type: Specific version type: internal, external, + external_gte. Valid choices: internal, external, external_gte, force """ for param in (index, id): if param in SKIP_IN_PATH: @@ -1608,7 +1613,9 @@ async def search( `_all` or empty string to perform the operation on all indices :arg doc_type: A comma-separated list of document types to search; leave empty to perform the operation on all types - :arg _source: + :arg _source: Indicates which source fields are returned for + matching documents. These fields are returned in the hits._source + property of the search response. :arg _source_excludes: A list of fields to exclude from the returned _source field :arg _source_includes: A list of fields to extract and return @@ -1637,19 +1644,27 @@ async def search( query (AND or OR) Valid choices: AND, OR Default: OR :arg df: The field to use as default where no field prefix is given in the query string - :arg docvalue_fields: + :arg docvalue_fields: Array of wildcard (*) patterns. The + request returns doc values for field names matching these patterns in + the hits.fields property of the response. :arg expand_wildcards: Whether to expand wildcard expression to concrete indices that are open, closed or both. Valid choices: open, closed, hidden, none, all Default: open - :arg explain: - :arg fields: - :arg from_: + :arg explain: If true, returns detailed information about score + computation as part of a hit. + :arg fields: Array of wildcard (*) patterns. The request returns + values for field names matching these patterns in the hits.fields + property of the response. + :arg from_: Starting document offset. By default, you cannot + page through more than 10,000 hits using the from and size parameters. + To page through more hits, use the search_after parameter. :arg highlight: :arg ignore_throttled: Whether specified concrete, expanded or aliased indices should be ignored when throttled :arg ignore_unavailable: Whether specified concrete indices should be ignored when unavailable (missing or closed) - :arg indices_boost: + :arg indices_boost: Boosts the _score of documents from + specified indices. :arg lenient: Specify whether format-based query failures (such as providing text to a numeric field) should be ignored :arg max_concurrent_shard_requests: The number of concurrent @@ -1659,8 +1674,10 @@ async def search( :arg min_compatible_shard_node: The minimum compatible version that all shards involved in search should have for this request to be successful - :arg min_score: - :arg pit: + :arg min_score: Minimum _score for matching documents. Documents + with a lower _score are not included in the search results. + :arg pit: Limits the search to a point in time (PIT). If you + provide a PIT, you cannot specify an in the request path. :arg post_filter: :arg pre_filter_shard_size: A threshold that enforces a pre- filter roundtrip to prefilter search shards based on query rewriting if @@ -1673,40 +1690,64 @@ async def search( be performed on (default: random) :arg profile: :arg q: Query in the Lucene query string syntax - :arg query: + :arg query: Defines the search definition using the Query DSL. :arg request_cache: Specify if request cache should be used for this request or not, defaults to index level setting :arg rescore: :arg rest_total_hits_as_int: Indicates whether hits.total should be rendered as an integer or an object in the rest search response :arg routing: A comma-separated list of specific routing values - :arg runtime_mappings: - :arg script_fields: + :arg runtime_mappings: Defines one or more runtime fields in the + search request. These fields take precedence over mapped fields with the + same name. + :arg script_fields: Retrieve a script evaluation (based on + different fields) for each hit. :arg scroll: Specify how long a consistent view of the index should be maintained for scrolled search :arg search_after: :arg search_type: Search operation type Valid choices: query_then_fetch, dfs_query_then_fetch - :arg seq_no_primary_term: - :arg size: + :arg seq_no_primary_term: If true, returns sequence number and + primary term of the last modification of each hit. See Optimistic + concurrency control. + :arg size: The number of hits to return. By default, you cannot + page through more than 10,000 hits using the from and size parameters. + To page through more hits, use the search_after parameter. :arg slice: :arg sort: - :arg stats: - :arg stored_fields: + :arg stats: Stats groups to associate with the search. Each + group maintains a statistics aggregation for its associated searches. + You can retrieve these stats using the indices stats API. + :arg stored_fields: List of stored fields to return as part of a + hit. If no fields are specified, no stored fields are included in the + response. If this field is specified, the _source parameter defaults to + false. You can pass _source: true to return both source fields and + stored fields in the search response. :arg suggest: - :arg suggest_field: Specify which field to use for suggestions + :arg suggest_field: Specifies which field to use for + suggestions. :arg suggest_mode: Specify suggest mode Valid choices: missing, popular, always Default: missing :arg suggest_size: How many suggestions to return in response :arg suggest_text: The source text for which the suggestions - should be returned - :arg terminate_after: - :arg timeout: - :arg track_scores: - :arg track_total_hits: + should be returned. + :arg terminate_after: Maximum number of documents to collect for + each shard. If a query reaches this limit, Elasticsearch terminates the + query early. Elasticsearch collects documents before sorting. Defaults + to 0, which does not terminate query execution early. + :arg timeout: Specifies the period of time to wait for a + response from each shard. If no response is received before the timeout + expires, the request fails and returns an error. Defaults to no timeout. + :arg track_scores: If true, calculate and return document + scores, even if the scores are not used for sorting. + :arg track_total_hits: Number of hits matching the query to + count accurately. If true, the exact number of hits is returned at the + cost of some performance. If false, the response does not include the + total number of hits matching the query. Defaults to 10,000 hits. :arg typed_keys: Specify whether aggregation and suggester names should be prefixed by their respective types in the response - :arg version: + :arg version: If true, returns document version as part of a + hit. """ if "from_" in params: params["from"] = params.pop("from_") @@ -1895,6 +1936,15 @@ async def termvectors( "routing", "timeout", "wait_for_active_shards", + body_params=[ + "_source", + "detect_noop", + "doc", + "doc_as_upsert", + "script", + "scripted_upsert", + "upsert", + ], ) async def update(self, index, id, body, doc_type=None, params=None, headers=None): """ @@ -1907,34 +1957,48 @@ async def update(self, index, id, body, doc_type=None, params=None, headers=None :arg body: The request definition requires either `script` or partial `doc` :arg doc_type: The type of the document - :arg _source: True or false to return the _source field or not, - or a list of fields to return - :arg _source_excludes: A list of fields to exclude from the - returned _source field - :arg _source_includes: A list of fields to extract and return - from the _source field - :arg if_primary_term: only perform the update operation if the - last operation that has changed the document has the specified primary - term - :arg if_seq_no: only perform the update operation if the last - operation that has changed the document has the specified sequence - number - :arg lang: The script language (default: painless) - :arg refresh: If `true` then refresh the affected shards to make - this operation visible to search, if `wait_for` then wait for a refresh - to make this operation visible to search, if `false` (the default) then - do nothing with refreshes. Valid choices: true, false, wait_for - :arg require_alias: When true, requires destination is an alias. - Default is false + :arg _source: Set to false to disable source retrieval. You can + also specify a comma-separated list of the fields you want to retrieve. + :arg _source_excludes: Specify the source fields you want to + exclude. + :arg _source_includes: Specify the source fields you want to + retrieve. + :arg detect_noop: Set to false to disable setting 'result' in + the response to 'noop' if no change to the document occurred. + :arg doc: A partial update to an existing document. + :arg doc_as_upsert: Set to true to use the contents of 'doc' as + the value of 'upsert' + :arg if_primary_term: Only perform the operation if the document + has this primary term. + :arg if_seq_no: Only perform the operation if the document has + this sequence number. + :arg lang: The script language. Default: painless + :arg refresh: If 'true', Elasticsearch refreshes the affected + shards to make this operation visible to search, if 'wait_for' then wait + for a refresh to make this operation visible to search, if 'false' do + nothing with refreshes. Valid choices: true, false, wait_for Default: + false + :arg require_alias: If true, the destination must be an index + alias. :arg retry_on_conflict: Specify how many times should the - operation be retried when a conflict occurs (default: 0) - :arg routing: Specific routing value - :arg timeout: Explicit operation timeout - :arg wait_for_active_shards: Sets the number of shard copies - that must be active before proceeding with the update operation. - Defaults to 1, meaning the primary shard only. Set to `all` for all - shard copies, otherwise set to any non-negative value less than or equal - to the total number of copies for the shard (number of replicas + 1) + operation be retried when a conflict occurs. + :arg routing: Custom value used to route operations to a + specific shard. + :arg script: Script to execute to update the document. + :arg scripted_upsert: Set to true to execute the script whether + or not the document exists. + :arg timeout: Period to wait for dynamic mapping updates and + active shards. This guarantees Elasticsearch waits for at least the + timeout before failing. The actual wait time could be longer, + particularly when multiple waits occur. Default: 1m + :arg upsert: If the document does not already exist, the + contents of 'upsert' are inserted as a new document. If the document + exists, the 'script' is executed. + :arg wait_for_active_shards: The number of shard copies that + must be active before proceeding with the operations. Set to 'all' or + any positive integer up to the total number of shards in the index + (number_of_replicas+1). Defaults to 1 meaning the primary shard. + Default: 1 """ for param in (index, id, body): if param in SKIP_IN_PATH: diff --git a/elasticsearch/_async/client/__init__.pyi b/elasticsearch/_async/client/__init__.pyi index 9dad0dff2..ff80b312a 100644 --- a/elasticsearch/_async/client/__init__.pyi +++ b/elasticsearch/_async/client/__init__.pyi @@ -154,17 +154,19 @@ class AsyncElasticsearch(object): async def create( self, *, - index: Any, - id: Any, - body: Mapping[str, Any], - doc_type: Optional[Any] = ..., - pipeline: Optional[Any] = ..., - refresh: Optional[Any] = ..., - routing: Optional[Any] = ..., - timeout: Optional[Any] = ..., - version: Optional[Any] = ..., - version_type: Optional[Any] = ..., - wait_for_active_shards: Optional[Any] = ..., + index: str, + id: str, + document: Any, + doc_type: Optional[str] = ..., + pipeline: Optional[str] = ..., + refresh: Optional[Union[Union[Literal["wait_for"], str], bool]] = ..., + routing: Optional[str] = ..., + timeout: Optional[Union[int, str]] = ..., + version: Optional[int] = ..., + version_type: Optional[ + Union[Literal["internal", "external", "external_gte", "force"], str] + ] = ..., + wait_for_active_shards: Optional[Union[Union[Literal["all"], str], int]] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., @@ -181,21 +183,23 @@ class AsyncElasticsearch(object): async def index( self, *, - index: Any, - body: Mapping[str, Any], - doc_type: Optional[Any] = ..., - id: Optional[Any] = ..., - if_primary_term: Optional[Any] = ..., - if_seq_no: Optional[Any] = ..., - op_type: Optional[Any] = ..., - pipeline: Optional[Any] = ..., - refresh: Optional[Any] = ..., + index: str, + document: Any, + doc_type: Optional[str] = ..., + id: Optional[str] = ..., + if_primary_term: Optional[int] = ..., + if_seq_no: Optional[int] = ..., + op_type: Optional[Union[Literal["index", "create"], str]] = ..., + pipeline: Optional[str] = ..., + refresh: Optional[Union[Union[Literal["wait_for"], str], bool]] = ..., require_alias: Optional[bool] = ..., - routing: Optional[Any] = ..., - timeout: Optional[Any] = ..., - version: Optional[Any] = ..., - version_type: Optional[Any] = ..., - wait_for_active_shards: Optional[Any] = ..., + routing: Optional[str] = ..., + timeout: Optional[Union[int, str]] = ..., + version: Optional[int] = ..., + version_type: Optional[ + Union[Literal["internal", "external", "external_gte", "force"], str] + ] = ..., + wait_for_active_shards: Optional[Union[Union[Literal["all"], str], int]] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., @@ -291,17 +295,19 @@ class AsyncElasticsearch(object): async def delete( self, *, - index: Any, - id: Any, - doc_type: Optional[Any] = ..., - if_primary_term: Optional[Any] = ..., - if_seq_no: Optional[Any] = ..., - refresh: Optional[Any] = ..., - routing: Optional[Any] = ..., - timeout: Optional[Any] = ..., - version: Optional[Any] = ..., - version_type: Optional[Any] = ..., - wait_for_active_shards: Optional[Any] = ..., + index: str, + id: str, + doc_type: Optional[str] = ..., + if_primary_term: Optional[int] = ..., + if_seq_no: Optional[int] = ..., + refresh: Optional[Union[Union[Literal["wait_for"], str], bool]] = ..., + routing: Optional[str] = ..., + timeout: Optional[Union[int, str]] = ..., + version: Optional[int] = ..., + version_type: Optional[ + Union[Literal["internal", "external", "external_gte", "force"], str] + ] = ..., + wait_for_active_shards: Optional[Union[Union[Literal["all"], str], int]] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., @@ -407,19 +413,21 @@ class AsyncElasticsearch(object): async def exists( self, *, - index: Any, - id: Any, - doc_type: Optional[Any] = ..., - _source: Optional[Any] = ..., - _source_excludes: Optional[Any] = ..., - _source_includes: Optional[Any] = ..., - preference: Optional[Any] = ..., + index: str, + id: str, + doc_type: Optional[str] = ..., + _source: Optional[Union[Union[List[str], str], bool]] = ..., + _source_excludes: Optional[Union[List[str], str]] = ..., + _source_includes: Optional[Union[List[str], str]] = ..., + preference: Optional[str] = ..., realtime: Optional[bool] = ..., refresh: Optional[bool] = ..., - routing: Optional[Any] = ..., - stored_fields: Optional[Any] = ..., - version: Optional[Any] = ..., - version_type: Optional[Any] = ..., + routing: Optional[str] = ..., + stored_fields: Optional[Union[List[str], str]] = ..., + version: Optional[int] = ..., + version_type: Optional[ + Union[Literal["internal", "external", "external_gte", "force"], str] + ] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., @@ -432,7 +440,7 @@ class AsyncElasticsearch(object): api_key: Optional[Union[str, Tuple[str, str]]] = ..., params: Optional[MutableMapping[str, Any]] = ..., headers: Optional[MutableMapping[str, str]] = ..., - ) -> bool: ... + ) -> Any: ... async def exists_source( self, *, @@ -519,19 +527,21 @@ class AsyncElasticsearch(object): async def get( self, *, - index: Any, - id: Any, - doc_type: Optional[Any] = ..., - _source: Optional[Any] = ..., - _source_excludes: Optional[Any] = ..., - _source_includes: Optional[Any] = ..., - preference: Optional[Any] = ..., + index: str, + id: str, + doc_type: Optional[str] = ..., + _source: Optional[Union[Union[List[str], str], bool]] = ..., + _source_excludes: Optional[Union[List[str], str]] = ..., + _source_includes: Optional[Union[List[str], str]] = ..., + preference: Optional[str] = ..., realtime: Optional[bool] = ..., refresh: Optional[bool] = ..., - routing: Optional[Any] = ..., - stored_fields: Optional[Any] = ..., - version: Optional[Any] = ..., - version_type: Optional[Any] = ..., + routing: Optional[str] = ..., + stored_fields: Optional[Union[List[str], str]] = ..., + version: Optional[int] = ..., + version_type: Optional[ + Union[Literal["internal", "external", "external_gte", "force"], str] + ] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., @@ -1020,22 +1030,28 @@ class AsyncElasticsearch(object): async def update( self, *, - index: Any, - id: Any, + index: str, + id: str, body: Mapping[str, Any], - doc_type: Optional[Any] = ..., - _source: Optional[Any] = ..., - _source_excludes: Optional[Any] = ..., - _source_includes: Optional[Any] = ..., - if_primary_term: Optional[Any] = ..., - if_seq_no: Optional[Any] = ..., - lang: Optional[Any] = ..., - refresh: Optional[Any] = ..., + doc_type: Optional[str] = ..., + _source: Optional[Union[Union[List[str], str], bool]] = ..., + _source_excludes: Optional[Union[List[str], str]] = ..., + _source_includes: Optional[Union[List[str], str]] = ..., + detect_noop: Optional[bool] = ..., + doc: Optional[Any] = ..., + doc_as_upsert: Optional[bool] = ..., + if_primary_term: Optional[int] = ..., + if_seq_no: Optional[int] = ..., + lang: Optional[str] = ..., + refresh: Optional[Union[Union[Literal["wait_for"], str], bool]] = ..., require_alias: Optional[bool] = ..., - retry_on_conflict: Optional[Any] = ..., - routing: Optional[Any] = ..., - timeout: Optional[Any] = ..., - wait_for_active_shards: Optional[Any] = ..., + retry_on_conflict: Optional[int] = ..., + routing: Optional[str] = ..., + script: Optional[Union[Mapping[str, Any], str]] = ..., + scripted_upsert: Optional[bool] = ..., + timeout: Optional[Union[int, str]] = ..., + upsert: Optional[Any] = ..., + wait_for_active_shards: Optional[Union[Union[Literal["all"], str], int]] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., diff --git a/elasticsearch/_async/client/transform.py b/elasticsearch/_async/client/transform.py index 38c67e343..05a9ec49e 100644 --- a/elasticsearch/_async/client/transform.py +++ b/elasticsearch/_async/client/transform.py @@ -104,19 +104,23 @@ async def get_transform_stats(self, transform_id, params=None, headers=None): ) @query_params() - async def preview_transform(self, body, params=None, headers=None): + async def preview_transform( + self, body=None, transform_id=None, params=None, headers=None + ): """ Previews a transform. ``_ :arg body: The definition for the transform to preview + :arg transform_id: The id of the transform to preview. """ - if body in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'body'.") - return await self.transport.perform_request( - "POST", "/_transform/_preview", params=params, headers=headers, body=body + "POST", + _make_path("_transform", transform_id, "_preview"), + params=params, + headers=headers, + body=body, ) @query_params("defer_validation") diff --git a/elasticsearch/_async/client/transform.pyi b/elasticsearch/_async/client/transform.pyi index 583f34151..b11441469 100644 --- a/elasticsearch/_async/client/transform.pyi +++ b/elasticsearch/_async/client/transform.pyi @@ -91,7 +91,8 @@ class TransformClient(NamespacedClient): async def preview_transform( self, *, - body: Mapping[str, Any], + body: Optional[Mapping[str, Any]] = ..., + transform_id: Optional[Any] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., diff --git a/elasticsearch/client/__init__.py b/elasticsearch/client/__init__.py index 4b8cc5a71..980e11f5a 100644 --- a/elasticsearch/client/__init__.py +++ b/elasticsearch/client/__init__.py @@ -300,6 +300,7 @@ def info(self, params=None, headers=None): "version", "version_type", "wait_for_active_shards", + body_name="document", ) def create(self, index, id, body, doc_type=None, params=None, headers=None): """ @@ -310,7 +311,7 @@ def create(self, index, id, body, doc_type=None, params=None, headers=None): :arg index: The name of the index :arg id: Document ID - :arg body: The document + :arg document: The document :arg doc_type: The type of the document :arg pipeline: The pipeline id to preprocess incoming documents with @@ -354,6 +355,7 @@ def create(self, index, id, body, doc_type=None, params=None, headers=None): "version", "version_type", "wait_for_active_shards", + body_name="document", ) def index(self, index, body, doc_type=None, id=None, params=None, headers=None): """ @@ -362,7 +364,7 @@ def index(self, index, body, doc_type=None, id=None, params=None, headers=None): ``_ :arg index: The name of the index - :arg body: The document + :arg document: The document :arg doc_type: The type of the document :arg id: Document ID :arg if_primary_term: only perform the index operation if the @@ -988,28 +990,31 @@ def get(self, index, id, doc_type=None, params=None, headers=None): ``_ - :arg index: The name of the index - :arg id: The document ID + :arg index: Name of the index that contains the document. + :arg id: Unique identifier of the document. :arg doc_type: The type of the document (use `_all` to fetch the first document matching the ID across all types) :arg _source: True or false to return the _source field or not, - or a list of fields to return - :arg _source_excludes: A list of fields to exclude from the - returned _source field - :arg _source_includes: A list of fields to extract and return - from the _source field - :arg preference: Specify the node or shard the operation should - be performed on (default: random) - :arg realtime: Specify whether to perform the operation in - realtime or search mode - :arg refresh: Refresh the shard containing the document before - performing the operation - :arg routing: Specific routing value + or a list of fields to return. + :arg _source_excludes: A comma-separated list of source fields + to exclude in the response. + :arg _source_includes: A comma-separated list of source fields + to include in the response. + :arg preference: Specifies the node or shard the operation + should be performed on. Random by default. + :arg realtime: Boolean) If true, the request is real-time as + opposed to near-real-time. Default: True + :arg refresh: If true, Elasticsearch refreshes the affected + shards to make this operation visible to search. If false, do nothing + with refreshes. + :arg routing: Target the specified primary shard. :arg stored_fields: A comma-separated list of stored fields to return in the response - :arg version: Explicit version number for concurrency control - :arg version_type: Specific version type Valid choices: - internal, external, external_gte, force + :arg version: Explicit version number for concurrency control. + The specified version must match the current version of the document for + the request to succeed. + :arg version_type: Specific version type: internal, external, + external_gte. Valid choices: internal, external, external_gte, force """ for param in (index, id): if param in SKIP_IN_PATH: @@ -1596,7 +1601,9 @@ def search(self, body=None, index=None, doc_type=None, params=None, headers=None `_all` or empty string to perform the operation on all indices :arg doc_type: A comma-separated list of document types to search; leave empty to perform the operation on all types - :arg _source: + :arg _source: Indicates which source fields are returned for + matching documents. These fields are returned in the hits._source + property of the search response. :arg _source_excludes: A list of fields to exclude from the returned _source field :arg _source_includes: A list of fields to extract and return @@ -1625,19 +1632,27 @@ def search(self, body=None, index=None, doc_type=None, params=None, headers=None query (AND or OR) Valid choices: AND, OR Default: OR :arg df: The field to use as default where no field prefix is given in the query string - :arg docvalue_fields: + :arg docvalue_fields: Array of wildcard (*) patterns. The + request returns doc values for field names matching these patterns in + the hits.fields property of the response. :arg expand_wildcards: Whether to expand wildcard expression to concrete indices that are open, closed or both. Valid choices: open, closed, hidden, none, all Default: open - :arg explain: - :arg fields: - :arg from_: + :arg explain: If true, returns detailed information about score + computation as part of a hit. + :arg fields: Array of wildcard (*) patterns. The request returns + values for field names matching these patterns in the hits.fields + property of the response. + :arg from_: Starting document offset. By default, you cannot + page through more than 10,000 hits using the from and size parameters. + To page through more hits, use the search_after parameter. :arg highlight: :arg ignore_throttled: Whether specified concrete, expanded or aliased indices should be ignored when throttled :arg ignore_unavailable: Whether specified concrete indices should be ignored when unavailable (missing or closed) - :arg indices_boost: + :arg indices_boost: Boosts the _score of documents from + specified indices. :arg lenient: Specify whether format-based query failures (such as providing text to a numeric field) should be ignored :arg max_concurrent_shard_requests: The number of concurrent @@ -1647,8 +1662,10 @@ def search(self, body=None, index=None, doc_type=None, params=None, headers=None :arg min_compatible_shard_node: The minimum compatible version that all shards involved in search should have for this request to be successful - :arg min_score: - :arg pit: + :arg min_score: Minimum _score for matching documents. Documents + with a lower _score are not included in the search results. + :arg pit: Limits the search to a point in time (PIT). If you + provide a PIT, you cannot specify an in the request path. :arg post_filter: :arg pre_filter_shard_size: A threshold that enforces a pre- filter roundtrip to prefilter search shards based on query rewriting if @@ -1661,40 +1678,64 @@ def search(self, body=None, index=None, doc_type=None, params=None, headers=None be performed on (default: random) :arg profile: :arg q: Query in the Lucene query string syntax - :arg query: + :arg query: Defines the search definition using the Query DSL. :arg request_cache: Specify if request cache should be used for this request or not, defaults to index level setting :arg rescore: :arg rest_total_hits_as_int: Indicates whether hits.total should be rendered as an integer or an object in the rest search response :arg routing: A comma-separated list of specific routing values - :arg runtime_mappings: - :arg script_fields: + :arg runtime_mappings: Defines one or more runtime fields in the + search request. These fields take precedence over mapped fields with the + same name. + :arg script_fields: Retrieve a script evaluation (based on + different fields) for each hit. :arg scroll: Specify how long a consistent view of the index should be maintained for scrolled search :arg search_after: :arg search_type: Search operation type Valid choices: query_then_fetch, dfs_query_then_fetch - :arg seq_no_primary_term: - :arg size: + :arg seq_no_primary_term: If true, returns sequence number and + primary term of the last modification of each hit. See Optimistic + concurrency control. + :arg size: The number of hits to return. By default, you cannot + page through more than 10,000 hits using the from and size parameters. + To page through more hits, use the search_after parameter. :arg slice: :arg sort: - :arg stats: - :arg stored_fields: + :arg stats: Stats groups to associate with the search. Each + group maintains a statistics aggregation for its associated searches. + You can retrieve these stats using the indices stats API. + :arg stored_fields: List of stored fields to return as part of a + hit. If no fields are specified, no stored fields are included in the + response. If this field is specified, the _source parameter defaults to + false. You can pass _source: true to return both source fields and + stored fields in the search response. :arg suggest: - :arg suggest_field: Specify which field to use for suggestions + :arg suggest_field: Specifies which field to use for + suggestions. :arg suggest_mode: Specify suggest mode Valid choices: missing, popular, always Default: missing :arg suggest_size: How many suggestions to return in response :arg suggest_text: The source text for which the suggestions - should be returned - :arg terminate_after: - :arg timeout: - :arg track_scores: - :arg track_total_hits: + should be returned. + :arg terminate_after: Maximum number of documents to collect for + each shard. If a query reaches this limit, Elasticsearch terminates the + query early. Elasticsearch collects documents before sorting. Defaults + to 0, which does not terminate query execution early. + :arg timeout: Specifies the period of time to wait for a + response from each shard. If no response is received before the timeout + expires, the request fails and returns an error. Defaults to no timeout. + :arg track_scores: If true, calculate and return document + scores, even if the scores are not used for sorting. + :arg track_total_hits: Number of hits matching the query to + count accurately. If true, the exact number of hits is returned at the + cost of some performance. If false, the response does not include the + total number of hits matching the query. Defaults to 10,000 hits. :arg typed_keys: Specify whether aggregation and suggester names should be prefixed by their respective types in the response - :arg version: + :arg version: If true, returns document version as part of a + hit. """ if "from_" in params: params["from"] = params.pop("from_") @@ -1883,6 +1924,15 @@ def termvectors( "routing", "timeout", "wait_for_active_shards", + body_params=[ + "_source", + "detect_noop", + "doc", + "doc_as_upsert", + "script", + "scripted_upsert", + "upsert", + ], ) def update(self, index, id, body, doc_type=None, params=None, headers=None): """ @@ -1895,34 +1945,48 @@ def update(self, index, id, body, doc_type=None, params=None, headers=None): :arg body: The request definition requires either `script` or partial `doc` :arg doc_type: The type of the document - :arg _source: True or false to return the _source field or not, - or a list of fields to return - :arg _source_excludes: A list of fields to exclude from the - returned _source field - :arg _source_includes: A list of fields to extract and return - from the _source field - :arg if_primary_term: only perform the update operation if the - last operation that has changed the document has the specified primary - term - :arg if_seq_no: only perform the update operation if the last - operation that has changed the document has the specified sequence - number - :arg lang: The script language (default: painless) - :arg refresh: If `true` then refresh the affected shards to make - this operation visible to search, if `wait_for` then wait for a refresh - to make this operation visible to search, if `false` (the default) then - do nothing with refreshes. Valid choices: true, false, wait_for - :arg require_alias: When true, requires destination is an alias. - Default is false + :arg _source: Set to false to disable source retrieval. You can + also specify a comma-separated list of the fields you want to retrieve. + :arg _source_excludes: Specify the source fields you want to + exclude. + :arg _source_includes: Specify the source fields you want to + retrieve. + :arg detect_noop: Set to false to disable setting 'result' in + the response to 'noop' if no change to the document occurred. + :arg doc: A partial update to an existing document. + :arg doc_as_upsert: Set to true to use the contents of 'doc' as + the value of 'upsert' + :arg if_primary_term: Only perform the operation if the document + has this primary term. + :arg if_seq_no: Only perform the operation if the document has + this sequence number. + :arg lang: The script language. Default: painless + :arg refresh: If 'true', Elasticsearch refreshes the affected + shards to make this operation visible to search, if 'wait_for' then wait + for a refresh to make this operation visible to search, if 'false' do + nothing with refreshes. Valid choices: true, false, wait_for Default: + false + :arg require_alias: If true, the destination must be an index + alias. :arg retry_on_conflict: Specify how many times should the - operation be retried when a conflict occurs (default: 0) - :arg routing: Specific routing value - :arg timeout: Explicit operation timeout - :arg wait_for_active_shards: Sets the number of shard copies - that must be active before proceeding with the update operation. - Defaults to 1, meaning the primary shard only. Set to `all` for all - shard copies, otherwise set to any non-negative value less than or equal - to the total number of copies for the shard (number of replicas + 1) + operation be retried when a conflict occurs. + :arg routing: Custom value used to route operations to a + specific shard. + :arg script: Script to execute to update the document. + :arg scripted_upsert: Set to true to execute the script whether + or not the document exists. + :arg timeout: Period to wait for dynamic mapping updates and + active shards. This guarantees Elasticsearch waits for at least the + timeout before failing. The actual wait time could be longer, + particularly when multiple waits occur. Default: 1m + :arg upsert: If the document does not already exist, the + contents of 'upsert' are inserted as a new document. If the document + exists, the 'script' is executed. + :arg wait_for_active_shards: The number of shard copies that + must be active before proceeding with the operations. Set to 'all' or + any positive integer up to the total number of shards in the index + (number_of_replicas+1). Defaults to 1 meaning the primary shard. + Default: 1 """ for param in (index, id, body): if param in SKIP_IN_PATH: diff --git a/elasticsearch/client/__init__.pyi b/elasticsearch/client/__init__.pyi index 0988bf9ae..097c84b1a 100644 --- a/elasticsearch/client/__init__.pyi +++ b/elasticsearch/client/__init__.pyi @@ -154,17 +154,19 @@ class Elasticsearch(object): def create( self, *, - index: Any, - id: Any, - body: Mapping[str, Any], - doc_type: Optional[Any] = ..., - pipeline: Optional[Any] = ..., - refresh: Optional[Any] = ..., - routing: Optional[Any] = ..., - timeout: Optional[Any] = ..., - version: Optional[Any] = ..., - version_type: Optional[Any] = ..., - wait_for_active_shards: Optional[Any] = ..., + index: str, + id: str, + document: Any, + doc_type: Optional[str] = ..., + pipeline: Optional[str] = ..., + refresh: Optional[Union[Union[Literal["wait_for"], str], bool]] = ..., + routing: Optional[str] = ..., + timeout: Optional[Union[int, str]] = ..., + version: Optional[int] = ..., + version_type: Optional[ + Union[Literal["internal", "external", "external_gte", "force"], str] + ] = ..., + wait_for_active_shards: Optional[Union[Union[Literal["all"], str], int]] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., @@ -181,21 +183,23 @@ class Elasticsearch(object): def index( self, *, - index: Any, - body: Mapping[str, Any], - doc_type: Optional[Any] = ..., - id: Optional[Any] = ..., - if_primary_term: Optional[Any] = ..., - if_seq_no: Optional[Any] = ..., - op_type: Optional[Any] = ..., - pipeline: Optional[Any] = ..., - refresh: Optional[Any] = ..., + index: str, + document: Any, + doc_type: Optional[str] = ..., + id: Optional[str] = ..., + if_primary_term: Optional[int] = ..., + if_seq_no: Optional[int] = ..., + op_type: Optional[Union[Literal["index", "create"], str]] = ..., + pipeline: Optional[str] = ..., + refresh: Optional[Union[Union[Literal["wait_for"], str], bool]] = ..., require_alias: Optional[bool] = ..., - routing: Optional[Any] = ..., - timeout: Optional[Any] = ..., - version: Optional[Any] = ..., - version_type: Optional[Any] = ..., - wait_for_active_shards: Optional[Any] = ..., + routing: Optional[str] = ..., + timeout: Optional[Union[int, str]] = ..., + version: Optional[int] = ..., + version_type: Optional[ + Union[Literal["internal", "external", "external_gte", "force"], str] + ] = ..., + wait_for_active_shards: Optional[Union[Union[Literal["all"], str], int]] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., @@ -291,17 +295,19 @@ class Elasticsearch(object): def delete( self, *, - index: Any, - id: Any, - doc_type: Optional[Any] = ..., - if_primary_term: Optional[Any] = ..., - if_seq_no: Optional[Any] = ..., - refresh: Optional[Any] = ..., - routing: Optional[Any] = ..., - timeout: Optional[Any] = ..., - version: Optional[Any] = ..., - version_type: Optional[Any] = ..., - wait_for_active_shards: Optional[Any] = ..., + index: str, + id: str, + doc_type: Optional[str] = ..., + if_primary_term: Optional[int] = ..., + if_seq_no: Optional[int] = ..., + refresh: Optional[Union[Union[Literal["wait_for"], str], bool]] = ..., + routing: Optional[str] = ..., + timeout: Optional[Union[int, str]] = ..., + version: Optional[int] = ..., + version_type: Optional[ + Union[Literal["internal", "external", "external_gte", "force"], str] + ] = ..., + wait_for_active_shards: Optional[Union[Union[Literal["all"], str], int]] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., @@ -407,19 +413,21 @@ class Elasticsearch(object): def exists( self, *, - index: Any, - id: Any, - doc_type: Optional[Any] = ..., - _source: Optional[Any] = ..., - _source_excludes: Optional[Any] = ..., - _source_includes: Optional[Any] = ..., - preference: Optional[Any] = ..., + index: str, + id: str, + doc_type: Optional[str] = ..., + _source: Optional[Union[Union[List[str], str], bool]] = ..., + _source_excludes: Optional[Union[List[str], str]] = ..., + _source_includes: Optional[Union[List[str], str]] = ..., + preference: Optional[str] = ..., realtime: Optional[bool] = ..., refresh: Optional[bool] = ..., - routing: Optional[Any] = ..., - stored_fields: Optional[Any] = ..., - version: Optional[Any] = ..., - version_type: Optional[Any] = ..., + routing: Optional[str] = ..., + stored_fields: Optional[Union[List[str], str]] = ..., + version: Optional[int] = ..., + version_type: Optional[ + Union[Literal["internal", "external", "external_gte", "force"], str] + ] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., @@ -432,7 +440,7 @@ class Elasticsearch(object): api_key: Optional[Union[str, Tuple[str, str]]] = ..., params: Optional[MutableMapping[str, Any]] = ..., headers: Optional[MutableMapping[str, str]] = ..., - ) -> bool: ... + ) -> Any: ... def exists_source( self, *, @@ -519,19 +527,21 @@ class Elasticsearch(object): def get( self, *, - index: Any, - id: Any, - doc_type: Optional[Any] = ..., - _source: Optional[Any] = ..., - _source_excludes: Optional[Any] = ..., - _source_includes: Optional[Any] = ..., - preference: Optional[Any] = ..., + index: str, + id: str, + doc_type: Optional[str] = ..., + _source: Optional[Union[Union[List[str], str], bool]] = ..., + _source_excludes: Optional[Union[List[str], str]] = ..., + _source_includes: Optional[Union[List[str], str]] = ..., + preference: Optional[str] = ..., realtime: Optional[bool] = ..., refresh: Optional[bool] = ..., - routing: Optional[Any] = ..., - stored_fields: Optional[Any] = ..., - version: Optional[Any] = ..., - version_type: Optional[Any] = ..., + routing: Optional[str] = ..., + stored_fields: Optional[Union[List[str], str]] = ..., + version: Optional[int] = ..., + version_type: Optional[ + Union[Literal["internal", "external", "external_gte", "force"], str] + ] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., @@ -1020,22 +1030,28 @@ class Elasticsearch(object): def update( self, *, - index: Any, - id: Any, + index: str, + id: str, body: Mapping[str, Any], - doc_type: Optional[Any] = ..., - _source: Optional[Any] = ..., - _source_excludes: Optional[Any] = ..., - _source_includes: Optional[Any] = ..., - if_primary_term: Optional[Any] = ..., - if_seq_no: Optional[Any] = ..., - lang: Optional[Any] = ..., - refresh: Optional[Any] = ..., + doc_type: Optional[str] = ..., + _source: Optional[Union[Union[List[str], str], bool]] = ..., + _source_excludes: Optional[Union[List[str], str]] = ..., + _source_includes: Optional[Union[List[str], str]] = ..., + detect_noop: Optional[bool] = ..., + doc: Optional[Any] = ..., + doc_as_upsert: Optional[bool] = ..., + if_primary_term: Optional[int] = ..., + if_seq_no: Optional[int] = ..., + lang: Optional[str] = ..., + refresh: Optional[Union[Union[Literal["wait_for"], str], bool]] = ..., require_alias: Optional[bool] = ..., - retry_on_conflict: Optional[Any] = ..., - routing: Optional[Any] = ..., - timeout: Optional[Any] = ..., - wait_for_active_shards: Optional[Any] = ..., + retry_on_conflict: Optional[int] = ..., + routing: Optional[str] = ..., + script: Optional[Union[Mapping[str, Any], str]] = ..., + scripted_upsert: Optional[bool] = ..., + timeout: Optional[Union[int, str]] = ..., + upsert: Optional[Any] = ..., + wait_for_active_shards: Optional[Union[Union[Literal["all"], str], int]] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., diff --git a/elasticsearch/client/transform.py b/elasticsearch/client/transform.py index 023eb98f6..b8a46a98d 100644 --- a/elasticsearch/client/transform.py +++ b/elasticsearch/client/transform.py @@ -104,19 +104,23 @@ def get_transform_stats(self, transform_id, params=None, headers=None): ) @query_params() - def preview_transform(self, body, params=None, headers=None): + def preview_transform( + self, body=None, transform_id=None, params=None, headers=None + ): """ Previews a transform. ``_ :arg body: The definition for the transform to preview + :arg transform_id: The id of the transform to preview. """ - if body in SKIP_IN_PATH: - raise ValueError("Empty value passed for a required argument 'body'.") - return self.transport.perform_request( - "POST", "/_transform/_preview", params=params, headers=headers, body=body + "POST", + _make_path("_transform", transform_id, "_preview"), + params=params, + headers=headers, + body=body, ) @query_params("defer_validation") diff --git a/elasticsearch/client/transform.pyi b/elasticsearch/client/transform.pyi index 4ab10ef09..e9653f66d 100644 --- a/elasticsearch/client/transform.pyi +++ b/elasticsearch/client/transform.pyi @@ -91,7 +91,8 @@ class TransformClient(NamespacedClient): def preview_transform( self, *, - body: Mapping[str, Any], + body: Optional[Mapping[str, Any]] = ..., + transform_id: Optional[Any] = ..., pretty: Optional[bool] = ..., human: Optional[bool] = ..., error_trace: Optional[bool] = ..., diff --git a/elasticsearch/client/utils.py b/elasticsearch/client/utils.py index a7fc7da05..fda131a57 100644 --- a/elasticsearch/client/utils.py +++ b/elasticsearch/client/utils.py @@ -131,6 +131,10 @@ def query_params(*es_query_params, **kwargs): """ body_params = kwargs.pop("body_params", None) body_only_params = set(body_params or ()) - set(es_query_params) + body_name = kwargs.pop("body_name", None) + + # There should be no APIs defined with both 'body_params' and a named body. + assert not (body_name and body_params) def _wrapper(func): @wraps(func) @@ -168,7 +172,8 @@ def _wrapped(*args, **kwargs): "The '%s' parameter%s %s only serialized in the request body " "and can't be combined with the 'body' parameter. Either stop using the " "'body' parameter and use keyword-arguments only or move the specified " - "parameters into the 'body'" + "parameters into the 'body'. See https://github.com/elastic/elasticsearch-py/" + "issues/1698 for more information" % ( params_prose, "s" if plural_params else "", @@ -179,11 +184,11 @@ def _wrapped(*args, **kwargs): # If there's no parameter overlap we still warn the user # that the 'body' parameter is deprecated for this API. if using_body_kwarg and body_params: - api_name = str(func.__name__) warnings.warn( "The 'body' parameter is deprecated for the '%s' API and " - "will be removed in 8.0.0. Instead use API parameters directly" - % api_name, + "will be removed in 8.0.0. Instead use API parameters directly. " + "See https://github.com/elastic/elasticsearch-py/issues/1698 for " + "more information" % str(func.__name__), DeprecationWarning, stacklevel=2, ) @@ -192,7 +197,9 @@ def _wrapped(*args, **kwargs): if using_positional_args: warnings.warn( "Using positional arguments for APIs is deprecated and will be " - "disabled in 8.0.0. Instead only use keyword arguments for all APIs", + "disabled in 8.0.0. Instead use only keyword arguments for all APIs. " + "See https://github.com/elastic/elasticsearch-py/issues/1698 for " + "more information", DeprecationWarning, stacklevel=2, ) @@ -203,9 +210,40 @@ def _wrapped(*args, **kwargs): for param in body_params: value = kwargs.pop(param, None) if value is not None: - body[param] = value + body[param.rstrip("_")] = value kwargs["body"] = body + # If there's a named body parameter then we transform it to 'body' + # for backwards compatibility with libraries like APM. + # Otherwise we warn the user about 'body' being deprecated. + if body_name: + if body_name in kwargs: + # If passed both 'body' and the named body param we raise an error. + if using_body_kwarg: + raise TypeError( + "Can't use '%s' and 'body' parameters together because '%s' " + "is an alias for 'body'. Instead you should only use the " + "'%s' parameter. See https://github.com/elastic/elasticsearch-py/" + "issues/1698 for more information" + % ( + body_name, + body_name, + body_name, + ) + ) + kwargs["body"] = kwargs.pop(body_name) + + # Warn if user passes 'body' but should be using the named body parameter. + elif using_body_kwarg: + warnings.warn( + "The 'body' parameter is deprecated for the '%s' API and " + "will be removed in 8.0.0. Instead use the '%s' parameter. " + "See https://github.com/elastic/elasticsearch-py/issues/1698 " + "for more information" % (str(func.__name__), body_name), + category=DeprecationWarning, + stacklevel=2, + ) + if http_auth is not None and api_key is not None: raise ValueError( "Only one of 'http_auth' and 'api_key' may be passed at a time" diff --git a/elasticsearch/client/utils.pyi b/elasticsearch/client/utils.pyi index a99b81f17..9bc752297 100644 --- a/elasticsearch/client/utils.pyi +++ b/elasticsearch/client/utils.pyi @@ -47,7 +47,9 @@ def _make_path(*parts: Any) -> str: ... GLOBAL_PARAMS: Tuple[str, ...] def query_params( - *es_query_params: str, body_params: Optional[List[str]] = ... + *es_query_params: str, + body_params: Optional[List[str]] = ..., + body_name: Optional[str] = ... ) -> Callable[[Callable[..., T]], Callable[..., T]]: ... def _bulk_body( serializer: Serializer, body: Union[str, bytes, Mapping[str, Any], Iterable[Any]] diff --git a/test_elasticsearch/test_client/test_utils.py b/test_elasticsearch/test_client/test_utils.py index c7ae81d3c..3d9b90dae 100644 --- a/test_elasticsearch/test_client/test_utils.py +++ b/test_elasticsearch/test_client/test_utils.py @@ -37,11 +37,17 @@ def func_to_wrap(self, *args, **kwargs): self.calls.append((args, kwargs)) @query_params( - "query_only", "query_and_body", body_params=["query_and_body", "body_only"] + "query_only", + "query_and_body", + body_params=["query_and_body", "body_only", "from_"], ) def func_with_body_params(self, *args, **kwargs): self.calls.append((args, kwargs)) + @query_params("query_only", body_name="named_body") + def func_with_named_body(self, *args, **kwargs): + self.calls.append((args, kwargs)) + def test_handles_params(self): self.func_to_wrap(params={"simple_param_2": "2"}, simple_param="3") self.assertEqual( @@ -200,7 +206,8 @@ def test_body_params_errors(self): "The 'body_only' parameter is only serialized in the request body " "and can't be combined with the 'body' parameter. Either stop using " "the 'body' parameter and use keyword-arguments only or move the " - "specified parameters into the 'body'" + "specified parameters into the 'body'. " + "See https://github.com/elastic/elasticsearch-py/issues/1698 for more information" ) # Positional arguments disable body serialization @@ -210,7 +217,8 @@ def test_body_params_errors(self): "The 'body_only' parameter is only serialized in the request body " "and can't be combined with the 'body' parameter. Either stop using " "the 'body' parameter and use keyword-arguments only or move the " - "specified parameters into the 'body'" + "specified parameters into the 'body'. " + "See https://github.com/elastic/elasticsearch-py/issues/1698 for more information" ) def test_body_params_deprecations(self): @@ -224,7 +232,8 @@ def test_body_params_deprecations(self): assert str(w[0].message) == ( "The 'body' parameter is deprecated for the " "'func_with_body_params' API and will be removed in 8.0.0. " - "Instead use API parameters directly" + "Instead use API parameters directly. " + "See https://github.com/elastic/elasticsearch-py/issues/1698 for more information" ) # APIs that don't have body parameters don't have a deprecated 'body' parameter @@ -243,7 +252,61 @@ def test_body_params_deprecations(self): assert w[0].category == DeprecationWarning assert str(w[0].message) == ( "Using positional arguments for APIs is deprecated and will be disabled in " - "8.0.0. Instead only use keyword arguments for all APIs" + "8.0.0. Instead use only keyword arguments for all APIs. See https://github.com/" + "elastic/elasticsearch-py/issues/1698 for more information" + ) + + def test_body_params_removes_underscore_suffix(self): + self.func_with_body_params(from_=0) + assert self.calls[-1] == ( + (), + {"body": {"from": 0}, "headers": {}, "params": {}}, + ) + + def test_named_body_params(self): + # Passing 'named_body' results in no error or warning + with warnings.catch_warnings(record=True) as w: + self.func_with_named_body(named_body=[]) + + assert self.calls[-1] == ((), {"body": [], "headers": {}, "params": {}}) + assert w == [] + + # Passing 'body' is a warning but works + with warnings.catch_warnings(record=True) as w: + self.func_with_named_body(body=[]) + + assert self.calls[-1] == ((), {"body": [], "headers": {}, "params": {}}) + assert len(w) == 1 + assert str(w[0].message) == ( + "The 'body' parameter is deprecated for the 'func_with_named_body' " + "API and will be removed in 8.0.0. Instead use the 'named_body' parameter. " + "See https://github.com/elastic/elasticsearch-py/issues/1698 for more information" + ) + + # Passing both 'named_body' and 'body' is an error + self.calls[:] = [] + with warnings.catch_warnings(record=True) as w: + with pytest.raises(TypeError) as e: + self.func_with_named_body(named_body=[], body=[]) + + assert self.calls == [] + assert w == [] + assert str(e.value) == ( + "Can't use 'named_body' and 'body' parameters together because 'named_body' " + "is an alias for 'body'. Instead you should only use the 'named_body' parameter. " + "See https://github.com/elastic/elasticsearch-py/issues/1698 for more information" + ) + + # Positional arguments aren't impacted. Only warning is for positional args + with warnings.catch_warnings(record=True) as w: + self.func_with_named_body([]) + + assert self.calls == [(([],), {"headers": {}, "params": {}})] + assert len(w) == 1 + assert str(w[0].message) == ( + "Using positional arguments for APIs is deprecated and will be disabled in " + "8.0.0. Instead use only keyword arguments for all APIs. " + "See https://github.com/elastic/elasticsearch-py/issues/1698 for more information" ) diff --git a/test_elasticsearch/test_server/test_rest_api_spec.py b/test_elasticsearch/test_server/test_rest_api_spec.py index 4e96695de..18ce6b9a1 100644 --- a/test_elasticsearch/test_server/test_rest_api_spec.py +++ b/test_elasticsearch/test_server/test_rest_api_spec.py @@ -111,6 +111,7 @@ APIS_WITH_BODY_FIELDS = { "search", + "update", } @@ -487,7 +488,9 @@ def sync_runner(sync_client): # Try loading the REST API test specs from the Elastic Artifacts API try: # Construct the HTTP and Elasticsearch client - http = urllib3.PoolManager(retries=10) + http = urllib3.PoolManager( + retries=10, headers=urllib3.util.make_headers(accept_encoding=True) + ) client = get_client() # Make a request to Elasticsearch for the build hash, we'll be looking for From 83b403a54f88e9f4bf33551579ca36b06126beb7 Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Mon, 30 Aug 2021 15:01:52 -0500 Subject: [PATCH 2/2] skip tests with intentionally bad input --- test_elasticsearch/test_server/test_rest_api_spec.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test_elasticsearch/test_server/test_rest_api_spec.py b/test_elasticsearch/test_server/test_rest_api_spec.py index 18ce6b9a1..8ace8127d 100644 --- a/test_elasticsearch/test_server/test_rest_api_spec.py +++ b/test_elasticsearch/test_server/test_rest_api_spec.py @@ -62,6 +62,9 @@ # broken YAML tests on some releases SKIP_TESTS = { + # Uses bad input intentionally + "update/90_error[0]", + "search/20_default_values[1]", # Warning about date_histogram.interval deprecation is raised randomly "search/aggregation/250_moving_fn[1]", # body: null