[7.x] Add the 'X-Elastic-Client-Meta' header
Co-authored-by: Seth Michael Larson <[email protected]>
This commit is contained in:
co-authored by
Seth Michael Larson
parent
2e06989ca1
commit
b894e359df
@@ -22,7 +22,11 @@ import urllib3 # type: ignore
|
|||||||
import warnings
|
import warnings
|
||||||
from ._extra_imports import aiohttp_exceptions, aiohttp, yarl
|
from ._extra_imports import aiohttp_exceptions, aiohttp, yarl
|
||||||
from .compat import get_running_loop
|
from .compat import get_running_loop
|
||||||
from ..connection import Connection
|
from ..connection.base import (
|
||||||
|
Connection,
|
||||||
|
_get_client_meta_header,
|
||||||
|
_python_to_meta_version,
|
||||||
|
)
|
||||||
from ..compat import urlencode
|
from ..compat import urlencode
|
||||||
from ..exceptions import (
|
from ..exceptions import (
|
||||||
ConnectionError,
|
ConnectionError,
|
||||||
@@ -218,6 +222,11 @@ class AIOHttpConnection(AsyncConnection):
|
|||||||
|
|
||||||
orig_body = body
|
orig_body = body
|
||||||
url_path = self.url_prefix + url
|
url_path = self.url_prefix + url
|
||||||
|
if params:
|
||||||
|
# Pop client metadata from parameters, if any.
|
||||||
|
client_meta = tuple(params.pop("_client_meta", ()))
|
||||||
|
else:
|
||||||
|
client_meta = ()
|
||||||
if params:
|
if params:
|
||||||
query_string = urlencode(params)
|
query_string = urlencode(params)
|
||||||
else:
|
else:
|
||||||
@@ -268,6 +277,13 @@ class AIOHttpConnection(AsyncConnection):
|
|||||||
body = self._gzip_compress(body)
|
body = self._gzip_compress(body)
|
||||||
req_headers["content-encoding"] = "gzip"
|
req_headers["content-encoding"] = "gzip"
|
||||||
|
|
||||||
|
# Create meta header for aiohttp
|
||||||
|
if self.meta_header:
|
||||||
|
client_meta = (
|
||||||
|
("ai", _python_to_meta_version(aiohttp.__version__)),
|
||||||
|
) + client_meta
|
||||||
|
req_headers["x-elastic-client-meta"] = _get_client_meta_header(client_meta)
|
||||||
|
|
||||||
start = self.loop.time()
|
start = self.loop.time()
|
||||||
try:
|
try:
|
||||||
async with self.session.request(
|
async with self.session.request(
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from ._extra_imports import aiohttp # type: ignore
|
from ._extra_imports import aiohttp # type: ignore
|
||||||
from typing import Optional, Mapping, Collection, Union, Any, Tuple
|
from typing import Optional, Mapping, MutableMapping, Collection, Union, Any, Tuple
|
||||||
from ..connection import Connection
|
from ..connection import Connection
|
||||||
|
|
||||||
class AsyncConnection(Connection):
|
class AsyncConnection(Connection):
|
||||||
@@ -24,11 +24,11 @@ class AsyncConnection(Connection):
|
|||||||
self,
|
self,
|
||||||
method: str,
|
method: str,
|
||||||
url: str,
|
url: str,
|
||||||
params: Optional[Mapping[str, Any]] = ...,
|
params: Optional[MutableMapping[str, Any]] = ...,
|
||||||
body: Optional[bytes] = ...,
|
body: Optional[bytes] = ...,
|
||||||
timeout: Optional[Union[int, float]] = ...,
|
timeout: Optional[Union[int, float]] = ...,
|
||||||
ignore: Collection[int] = ...,
|
ignore: Collection[int] = ...,
|
||||||
headers: Optional[Mapping[str, str]] = ...,
|
headers: Optional[MutableMapping[str, str]] = ...,
|
||||||
) -> Tuple[int, Mapping[str, str], str]: ...
|
) -> Tuple[int, Mapping[str, str], str]: ...
|
||||||
async def close(self) -> None: ...
|
async def close(self) -> None: ...
|
||||||
|
|
||||||
@@ -55,6 +55,7 @@ class AIOHttpConnection(AsyncConnection):
|
|||||||
cloud_id: Optional[str] = ...,
|
cloud_id: Optional[str] = ...,
|
||||||
api_key: Optional[Any] = ...,
|
api_key: Optional[Any] = ...,
|
||||||
opaque_id: Optional[str] = ...,
|
opaque_id: Optional[str] = ...,
|
||||||
|
meta_header: bool = ...,
|
||||||
loop: Any = ...,
|
loop: Any = ...,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import gzip
|
|||||||
import io
|
import io
|
||||||
import re
|
import re
|
||||||
from platform import python_version
|
from platform import python_version
|
||||||
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -65,6 +66,8 @@ class Connection(object):
|
|||||||
:arg cloud_id: The Cloud ID from ElasticCloud. Convenient way to connect to cloud instances.
|
:arg cloud_id: The Cloud ID from ElasticCloud. Convenient way to connect to cloud instances.
|
||||||
:arg opaque_id: Send this value in the 'X-Opaque-Id' HTTP header
|
:arg opaque_id: Send this value in the 'X-Opaque-Id' HTTP header
|
||||||
For tracing all requests made by this transport.
|
For tracing all requests made by this transport.
|
||||||
|
:arg meta_header: If True will send the 'X-Elastic-Client-Meta' HTTP header containing
|
||||||
|
simple client metadata. Setting to False will disable the header. Defaults to True.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -79,6 +82,7 @@ class Connection(object):
|
|||||||
cloud_id=None,
|
cloud_id=None,
|
||||||
api_key=None,
|
api_key=None,
|
||||||
opaque_id=None,
|
opaque_id=None,
|
||||||
|
meta_header=True,
|
||||||
**kwargs
|
**kwargs
|
||||||
):
|
):
|
||||||
|
|
||||||
@@ -148,6 +152,10 @@ class Connection(object):
|
|||||||
self.url_prefix = url_prefix
|
self.url_prefix = url_prefix
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
|
||||||
|
if not isinstance(meta_header, bool):
|
||||||
|
raise TypeError("meta_header must be of type bool")
|
||||||
|
self.meta_header = meta_header
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<%s: %s>" % (self.__class__.__name__, self.host)
|
return "<%s: %s>" % (self.__class__.__name__, self.host)
|
||||||
|
|
||||||
@@ -329,3 +337,25 @@ class Connection(object):
|
|||||||
s = "{0}:{1}".format(api_key[0], api_key[1]).encode("utf-8")
|
s = "{0}:{1}".format(api_key[0], api_key[1]).encode("utf-8")
|
||||||
return "ApiKey " + binascii.b2a_base64(s).rstrip(b"\r\n").decode("utf-8")
|
return "ApiKey " + binascii.b2a_base64(s).rstrip(b"\r\n").decode("utf-8")
|
||||||
return "ApiKey " + api_key
|
return "ApiKey " + api_key
|
||||||
|
|
||||||
|
|
||||||
|
def _python_to_meta_version(version):
|
||||||
|
"""Transforms a Python package version to one
|
||||||
|
compatible with 'X-Elastic-Client-Meta'. Essentially
|
||||||
|
replaces any pre-release information with a 'p' suffix.
|
||||||
|
"""
|
||||||
|
version, version_pre = re.match(r"^([0-9.]+)(.*)$", version).groups()
|
||||||
|
if version_pre:
|
||||||
|
version += "p"
|
||||||
|
return version
|
||||||
|
|
||||||
|
|
||||||
|
def _get_client_meta_header(client_meta=()):
|
||||||
|
"""Builds an 'X-Elastic-Client-Meta' HTTP header"""
|
||||||
|
es_version = _python_to_meta_version(__versionstr__)
|
||||||
|
py_version = python_version() + ("p" if sys.version_info[3] != "final" else "")
|
||||||
|
# First three values have to be 'service', 'language', 'transport'
|
||||||
|
client_meta = (("es", es_version), ("py", py_version), ("t", es_version)) + tuple(
|
||||||
|
client_meta
|
||||||
|
)
|
||||||
|
return ",".join("%s=%s" % (k, v) for k, v in client_meta)
|
||||||
|
|||||||
@@ -21,13 +21,13 @@ from typing import (
|
|||||||
Union,
|
Union,
|
||||||
Optional,
|
Optional,
|
||||||
Mapping,
|
Mapping,
|
||||||
|
MutableMapping,
|
||||||
Tuple,
|
Tuple,
|
||||||
List,
|
List,
|
||||||
NoReturn,
|
NoReturn,
|
||||||
Dict,
|
Dict,
|
||||||
Sequence,
|
Sequence,
|
||||||
Any,
|
Any,
|
||||||
AnyStr,
|
|
||||||
Collection,
|
Collection,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -44,6 +44,7 @@ class Connection(object):
|
|||||||
host: str
|
host: str
|
||||||
url_prefix: str
|
url_prefix: str
|
||||||
timeout: Optional[Union[float, int]]
|
timeout: Optional[Union[float, int]]
|
||||||
|
meta_header: bool
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
host: str = ...,
|
host: str = ...,
|
||||||
@@ -56,6 +57,7 @@ class Connection(object):
|
|||||||
cloud_id: Optional[str] = ...,
|
cloud_id: Optional[str] = ...,
|
||||||
api_key: Optional[Union[Tuple[str, str], List[str], str]] = ...,
|
api_key: Optional[Union[Tuple[str, str], List[str], str]] = ...,
|
||||||
opaque_id: Optional[str] = ...,
|
opaque_id: Optional[str] = ...,
|
||||||
|
meta_header: bool = ...,
|
||||||
**kwargs: Any
|
**kwargs: Any
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
def __repr__(self) -> str: ...
|
def __repr__(self) -> str: ...
|
||||||
@@ -77,11 +79,11 @@ class Connection(object):
|
|||||||
self,
|
self,
|
||||||
method: str,
|
method: str,
|
||||||
url: str,
|
url: str,
|
||||||
params: Optional[Mapping[str, Any]] = ...,
|
params: Optional[MutableMapping[str, Any]] = ...,
|
||||||
body: Optional[bytes] = ...,
|
body: Optional[bytes] = ...,
|
||||||
timeout: Optional[Union[int, float]] = ...,
|
timeout: Optional[Union[int, float]] = ...,
|
||||||
ignore: Collection[int] = ...,
|
ignore: Collection[int] = ...,
|
||||||
headers: Optional[Mapping[str, str]] = ...,
|
headers: Optional[MutableMapping[str, str]] = ...,
|
||||||
) -> Tuple[int, Mapping[str, str], str]: ...
|
) -> Tuple[int, Mapping[str, str], str]: ...
|
||||||
def log_request_success(
|
def log_request_success(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
REQUESTS_AVAILABLE = False
|
REQUESTS_AVAILABLE = False
|
||||||
|
|
||||||
from .base import Connection
|
from .base import Connection, _get_client_meta_header, _python_to_meta_version
|
||||||
from ..exceptions import (
|
from ..exceptions import (
|
||||||
ConnectionError,
|
ConnectionError,
|
||||||
ImproperlyConfigured,
|
ImproperlyConfigured,
|
||||||
@@ -142,13 +142,25 @@ class RequestsHttpConnection(Connection):
|
|||||||
url = self.base_url + url
|
url = self.base_url + url
|
||||||
headers = headers or {}
|
headers = headers or {}
|
||||||
if params:
|
if params:
|
||||||
url = "%s?%s" % (url, urlencode(params or {}))
|
# Pop client metadata from parameters, if any.
|
||||||
|
client_meta = params.pop("_client_meta", ())
|
||||||
|
else:
|
||||||
|
client_meta = ()
|
||||||
|
if params:
|
||||||
|
url = "%s?%s" % (url, urlencode(params))
|
||||||
|
|
||||||
orig_body = body
|
orig_body = body
|
||||||
if self.http_compress and body:
|
if self.http_compress and body:
|
||||||
body = self._gzip_compress(body)
|
body = self._gzip_compress(body)
|
||||||
headers["content-encoding"] = "gzip"
|
headers["content-encoding"] = "gzip"
|
||||||
|
|
||||||
|
# Create meta header for requests
|
||||||
|
if self.meta_header:
|
||||||
|
client_meta = (
|
||||||
|
("rq", _python_to_meta_version(requests.__version__)),
|
||||||
|
) + client_meta
|
||||||
|
headers["x-elastic-client-meta"] = _get_client_meta_header(client_meta)
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
request = requests.Request(method=method, headers=headers, url=url, data=body)
|
request = requests.Request(method=method, headers=headers, url=url, data=body)
|
||||||
prepared_request = self.session.prepare_request(request)
|
prepared_request = self.session.prepare_request(request)
|
||||||
|
|||||||
@@ -37,5 +37,6 @@ class RequestsHttpConnection(Connection):
|
|||||||
cloud_id: Optional[str] = ...,
|
cloud_id: Optional[str] = ...,
|
||||||
api_key: Optional[Any] = ...,
|
api_key: Optional[Any] = ...,
|
||||||
opaque_id: Optional[str] = ...,
|
opaque_id: Optional[str] = ...,
|
||||||
|
meta_header: bool = ...,
|
||||||
**kwargs: Any
|
**kwargs: Any
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from urllib3.exceptions import ReadTimeoutError, SSLError as UrllibSSLError # t
|
|||||||
from urllib3.util.retry import Retry # type: ignore
|
from urllib3.util.retry import Retry # type: ignore
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from .base import Connection
|
from .base import Connection, _get_client_meta_header, _python_to_meta_version
|
||||||
from ..exceptions import (
|
from ..exceptions import (
|
||||||
ConnectionError,
|
ConnectionError,
|
||||||
ImproperlyConfigured,
|
ImproperlyConfigured,
|
||||||
@@ -216,8 +216,14 @@ class Urllib3HttpConnection(Connection):
|
|||||||
self, method, url, params=None, body=None, timeout=None, ignore=(), headers=None
|
self, method, url, params=None, body=None, timeout=None, ignore=(), headers=None
|
||||||
):
|
):
|
||||||
url = self.url_prefix + url
|
url = self.url_prefix + url
|
||||||
|
# Pop client metadata from parameters, if any.
|
||||||
|
if params:
|
||||||
|
client_meta = tuple(params.pop("_client_meta", ()))
|
||||||
|
else:
|
||||||
|
client_meta = ()
|
||||||
if params:
|
if params:
|
||||||
url = "%s?%s" % (url, urlencode(params))
|
url = "%s?%s" % (url, urlencode(params))
|
||||||
|
|
||||||
full_url = self.host + url
|
full_url = self.host + url
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
@@ -242,6 +248,15 @@ class Urllib3HttpConnection(Connection):
|
|||||||
body = self._gzip_compress(body)
|
body = self._gzip_compress(body)
|
||||||
request_headers["content-encoding"] = "gzip"
|
request_headers["content-encoding"] = "gzip"
|
||||||
|
|
||||||
|
# Create meta header for urllib3
|
||||||
|
if self.meta_header:
|
||||||
|
client_meta = (
|
||||||
|
("ur", _python_to_meta_version(urllib3.__version__)),
|
||||||
|
) + client_meta
|
||||||
|
request_headers["x-elastic-client-meta"] = _get_client_meta_header(
|
||||||
|
client_meta
|
||||||
|
)
|
||||||
|
|
||||||
response = self.pool.urlopen(
|
response = self.pool.urlopen(
|
||||||
method, url, body, retries=Retry(False), headers=request_headers, **kw
|
method, url, body, retries=Retry(False), headers=request_headers, **kw
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -51,5 +51,6 @@ class Urllib3HttpConnection(Connection):
|
|||||||
cloud_id: Optional[str] = ...,
|
cloud_id: Optional[str] = ...,
|
||||||
api_key: Optional[Any] = ...,
|
api_key: Optional[Any] = ...,
|
||||||
opaque_id: Optional[str] = ...,
|
opaque_id: Optional[str] = ...,
|
||||||
|
meta_header: bool = ...,
|
||||||
**kwargs: Any
|
**kwargs: Any
|
||||||
) -> None: ...
|
) -> None: ...
|
||||||
|
|||||||
@@ -230,6 +230,8 @@ def _process_bulk_chunk(
|
|||||||
"""
|
"""
|
||||||
Send a bulk request to elasticsearch and process the output.
|
Send a bulk request to elasticsearch and process the output.
|
||||||
"""
|
"""
|
||||||
|
kwargs = _add_helper_meta_to_kwargs(kwargs, "bp")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# send the actual request
|
# send the actual request
|
||||||
resp = client.bulk("\n".join(bulk_actions) + "\n", *args, **kwargs)
|
resp = client.bulk("\n".join(bulk_actions) + "\n", *args, **kwargs)
|
||||||
@@ -248,6 +250,13 @@ def _process_bulk_chunk(
|
|||||||
yield item
|
yield item
|
||||||
|
|
||||||
|
|
||||||
|
def _add_helper_meta_to_kwargs(kwargs, helper_meta):
|
||||||
|
params = (kwargs or {}).pop("params", {})
|
||||||
|
params["_client_meta"] = (("h", helper_meta),)
|
||||||
|
kwargs["params"] = params
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
def streaming_bulk(
|
def streaming_bulk(
|
||||||
client,
|
client,
|
||||||
actions,
|
actions,
|
||||||
@@ -515,6 +524,7 @@ def scan(
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
scroll_kwargs = scroll_kwargs or {}
|
scroll_kwargs = scroll_kwargs or {}
|
||||||
|
_add_helper_meta_to_kwargs(scroll_kwargs, "s")
|
||||||
|
|
||||||
if not preserve_order:
|
if not preserve_order:
|
||||||
query = query.copy() if query else {}
|
query = query.copy() if query else {}
|
||||||
@@ -562,7 +572,11 @@ def scan(
|
|||||||
|
|
||||||
finally:
|
finally:
|
||||||
if scroll_id and clear_scroll:
|
if scroll_id and clear_scroll:
|
||||||
client.clear_scroll(body={"scroll_id": [scroll_id]}, ignore=(404,))
|
client.clear_scroll(
|
||||||
|
body={"scroll_id": [scroll_id]},
|
||||||
|
ignore=(404,),
|
||||||
|
params={"_client_meta": (("h", "s"),)},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def reindex(
|
def reindex(
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import ssl
|
import ssl
|
||||||
|
import re
|
||||||
import gzip
|
import gzip
|
||||||
import io
|
import io
|
||||||
from mock import patch
|
from mock import patch
|
||||||
@@ -316,3 +317,43 @@ class TestAIOHttpConnection:
|
|||||||
con = await self._get_mock_connection(response_body=buf)
|
con = await self._get_mock_connection(response_body=buf)
|
||||||
status, headers, data = await con.perform_request("GET", "/")
|
status, headers, data = await con.perform_request("GET", "/")
|
||||||
assert u"你好\uda6a" == data
|
assert u"你好\uda6a" == data
|
||||||
|
|
||||||
|
async def test_meta_header_value(self):
|
||||||
|
con = await self._get_mock_connection()
|
||||||
|
assert con.meta_header is True
|
||||||
|
|
||||||
|
await con.perform_request("GET", "/", body=b"{}")
|
||||||
|
|
||||||
|
_, kwargs = con.session.request.call_args
|
||||||
|
headers = kwargs["headers"]
|
||||||
|
assert re.match(
|
||||||
|
r"^es=[0-9]+\.[0-9]+\.[0-9]+p?,py=[0-9]+\.[0-9]+\.[0-9]+p?,"
|
||||||
|
r"t=[0-9]+\.[0-9]+\.[0-9]+p?,ai=[0-9]+\.[0-9]+\.[0-9]+p?$",
|
||||||
|
headers["x-elastic-client-meta"],
|
||||||
|
)
|
||||||
|
|
||||||
|
con = await self._get_mock_connection()
|
||||||
|
assert con.meta_header is True
|
||||||
|
|
||||||
|
await con.perform_request(
|
||||||
|
"GET", "/", body=b"{}", params={"_client_meta": (("h", "bp"),)}
|
||||||
|
)
|
||||||
|
|
||||||
|
(method, url), kwargs = con.session.request.call_args
|
||||||
|
headers = kwargs["headers"]
|
||||||
|
assert method == "GET"
|
||||||
|
assert str(url) == "http://localhost:9200/"
|
||||||
|
assert re.match(
|
||||||
|
r"^es=[0-9]+\.[0-9]+\.[0-9]+p?,py=[0-9]+\.[0-9]+\.[0-9]+p?,"
|
||||||
|
r"t=[0-9]+\.[0-9]+\.[0-9]+p?,ai=[0-9]+\.[0-9]+\.[0-9]+p?,h=bp$",
|
||||||
|
headers["x-elastic-client-meta"],
|
||||||
|
)
|
||||||
|
|
||||||
|
con = await self._get_mock_connection(connection_params={"meta_header": False})
|
||||||
|
assert con.meta_header is False
|
||||||
|
|
||||||
|
await con.perform_request("GET", "/", body=b"{}")
|
||||||
|
|
||||||
|
_, kwargs = con.session.request.call_args
|
||||||
|
headers = kwargs["headers"]
|
||||||
|
assert "x-elastic-client-meta" not in (x.lower() for x in headers)
|
||||||
|
|||||||
@@ -40,7 +40,12 @@ from elasticsearch.connection import (
|
|||||||
RequestsHttpConnection,
|
RequestsHttpConnection,
|
||||||
Urllib3HttpConnection,
|
Urllib3HttpConnection,
|
||||||
)
|
)
|
||||||
from elasticsearch import __versionstr__
|
from elasticsearch.connection.base import (
|
||||||
|
_get_client_meta_header,
|
||||||
|
_python_to_meta_version,
|
||||||
|
)
|
||||||
|
|
||||||
|
from elasticsearch import __version__, __versionstr__
|
||||||
from .test_cases import TestCase, SkipTest
|
from .test_cases import TestCase, SkipTest
|
||||||
|
|
||||||
|
|
||||||
@@ -161,6 +166,32 @@ class TestBaseConnection(TestCase):
|
|||||||
conn = Connection(**kwargs)
|
conn = Connection(**kwargs)
|
||||||
assert conn.host == expected_host
|
assert conn.host == expected_host
|
||||||
|
|
||||||
|
def test_meta_header(self):
|
||||||
|
conn = Connection(meta_header=True)
|
||||||
|
assert conn.meta_header is True
|
||||||
|
conn = Connection(meta_header=False)
|
||||||
|
assert conn.meta_header is False
|
||||||
|
|
||||||
|
with pytest.raises(TypeError) as e:
|
||||||
|
Connection(meta_header=1)
|
||||||
|
assert str(e.value) == "meta_header must be of type bool"
|
||||||
|
|
||||||
|
def test_get_client_meta_header(self):
|
||||||
|
meta_header = _get_client_meta_header()
|
||||||
|
assert ("es=%s" % (".".join(str(x) for x in __version__),)) in meta_header
|
||||||
|
assert ("t=%s" % (".".join(str(x) for x in __version__),)) in meta_header
|
||||||
|
assert ("py=%s" % python_version()) in meta_header
|
||||||
|
|
||||||
|
meta_header = _get_client_meta_header((("h", "bp"),))
|
||||||
|
assert meta_header.endswith(",h=bp")
|
||||||
|
|
||||||
|
meta_header = _get_client_meta_header((("ur", "1.26.3"), ("h", "bp")))
|
||||||
|
assert meta_header.endswith(",ur=1.26.3,h=bp")
|
||||||
|
|
||||||
|
def test_python_to_meta_version(self):
|
||||||
|
assert _python_to_meta_version("1.26.3") == "1.26.3"
|
||||||
|
assert _python_to_meta_version("7.10.1a1") == "7.10.1p"
|
||||||
|
|
||||||
|
|
||||||
class TestUrllib3Connection(TestCase):
|
class TestUrllib3Connection(TestCase):
|
||||||
def _get_mock_connection(self, connection_params={}, response_body=b"{}"):
|
def _get_mock_connection(self, connection_params={}, response_body=b"{}"):
|
||||||
@@ -434,6 +465,45 @@ class TestUrllib3Connection(TestCase):
|
|||||||
status, headers, data = con.perform_request("GET", "/")
|
status, headers, data = con.perform_request("GET", "/")
|
||||||
self.assertEqual(u"你好\uda6a", data)
|
self.assertEqual(u"你好\uda6a", data)
|
||||||
|
|
||||||
|
def test_meta_header_value(self):
|
||||||
|
con = self._get_mock_connection()
|
||||||
|
self.assertTrue(con.meta_header)
|
||||||
|
|
||||||
|
con.perform_request("GET", "/", body=b"{}")
|
||||||
|
|
||||||
|
_, kwargs = con.pool.urlopen.call_args
|
||||||
|
headers = kwargs["headers"]
|
||||||
|
assert re.match(
|
||||||
|
r"^es=[0-9]+\.[0-9]+\.[0-9]+p?,py=[0-9]+\.[0-9]+\.[0-9]+p?,"
|
||||||
|
r"t=[0-9]+\.[0-9]+\.[0-9]+p?,ur=[0-9]+\.[0-9]+\.[0-9]+p?$",
|
||||||
|
headers["x-elastic-client-meta"],
|
||||||
|
)
|
||||||
|
|
||||||
|
con = self._get_mock_connection({"meta_header": True})
|
||||||
|
self.assertTrue(con.meta_header)
|
||||||
|
|
||||||
|
con.perform_request(
|
||||||
|
"GET", "/", body=b"{}", params={"_client_meta": (("h", "bp"),)}
|
||||||
|
)
|
||||||
|
|
||||||
|
args, kwargs = con.pool.urlopen.call_args
|
||||||
|
headers = kwargs["headers"]
|
||||||
|
assert args == ("GET", "/", b"{}")
|
||||||
|
assert re.match(
|
||||||
|
r"^es=[0-9]+\.[0-9]+\.[0-9]+p?,py=[0-9]+\.[0-9]+\.[0-9]+p?,"
|
||||||
|
r"t=[0-9]+\.[0-9]+\.[0-9]+p?,ur=[0-9]+\.[0-9]+\.[0-9]+p?,h=bp$",
|
||||||
|
headers["x-elastic-client-meta"],
|
||||||
|
)
|
||||||
|
|
||||||
|
con = self._get_mock_connection({"meta_header": False})
|
||||||
|
self.assertFalse(con.meta_header)
|
||||||
|
|
||||||
|
con.perform_request("GET", "/", body=b"{}")
|
||||||
|
|
||||||
|
_, kwargs = con.pool.urlopen.call_args
|
||||||
|
headers = kwargs["headers"]
|
||||||
|
assert "x-elastic-client-meta" not in (x.lower() for x in headers)
|
||||||
|
|
||||||
|
|
||||||
class TestRequestsConnection(TestCase):
|
class TestRequestsConnection(TestCase):
|
||||||
def _get_mock_connection(
|
def _get_mock_connection(
|
||||||
@@ -835,3 +905,42 @@ class TestRequestsConnection(TestCase):
|
|||||||
con = self._get_mock_connection(response_body=buf)
|
con = self._get_mock_connection(response_body=buf)
|
||||||
status, headers, data = con.perform_request("GET", "/")
|
status, headers, data = con.perform_request("GET", "/")
|
||||||
self.assertEqual(u"你好\uda6a", data)
|
self.assertEqual(u"你好\uda6a", data)
|
||||||
|
|
||||||
|
def test_meta_header_value(self):
|
||||||
|
con = self._get_mock_connection()
|
||||||
|
self.assertTrue(con.meta_header)
|
||||||
|
|
||||||
|
con.perform_request("GET", "/", body=b"{}")
|
||||||
|
|
||||||
|
args, _ = con.session.send.call_args
|
||||||
|
headers = args[0].headers
|
||||||
|
assert re.match(
|
||||||
|
r"^es=[0-9]+\.[0-9]+\.[0-9]+p?,py=[0-9]+\.[0-9]+\.[0-9]+p?,"
|
||||||
|
r"t=[0-9]+\.[0-9]+\.[0-9]+p?,rq=[0-9]+\.[0-9]+\.[0-9]+p?$",
|
||||||
|
headers["x-elastic-client-meta"],
|
||||||
|
)
|
||||||
|
|
||||||
|
con = self._get_mock_connection()
|
||||||
|
self.assertTrue(con.meta_header)
|
||||||
|
|
||||||
|
con.perform_request(
|
||||||
|
"GET", "/", body=b"{}", params={"_client_meta": (("h", "bp"),)}
|
||||||
|
)
|
||||||
|
|
||||||
|
args, _ = con.session.send.call_args
|
||||||
|
headers = args[0].headers
|
||||||
|
assert args[0].url == "http://localhost:9200/"
|
||||||
|
assert re.match(
|
||||||
|
r"^es=[0-9]+\.[0-9]+\.[0-9]+p?,py=[0-9]+\.[0-9]+\.[0-9]+p?,"
|
||||||
|
r"t=[0-9]+\.[0-9]+\.[0-9]+p?,rq=[0-9]+\.[0-9]+\.[0-9]+p?,h=bp$",
|
||||||
|
headers["x-elastic-client-meta"],
|
||||||
|
)
|
||||||
|
|
||||||
|
con = self._get_mock_connection({"meta_header": False})
|
||||||
|
self.assertFalse(con.meta_header)
|
||||||
|
|
||||||
|
con.perform_request("GET", "/", body=b"{}")
|
||||||
|
|
||||||
|
args, _ = con.session.send.call_args
|
||||||
|
headers = args[0].headers
|
||||||
|
assert "x-elastic-client-meta" not in (x.lower() for x in headers)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import time
|
|||||||
import threading
|
import threading
|
||||||
import pytest
|
import pytest
|
||||||
from elasticsearch import helpers, Elasticsearch
|
from elasticsearch import helpers, Elasticsearch
|
||||||
|
from elasticsearch.helpers import actions
|
||||||
from elasticsearch.serializer import JSONSerializer
|
from elasticsearch.serializer import JSONSerializer
|
||||||
|
|
||||||
from .test_cases import TestCase
|
from .test_cases import TestCase
|
||||||
@@ -204,6 +205,20 @@ class TestChunkActions(TestCase):
|
|||||||
chunk = chunk if isinstance(chunk, str) else chunk.encode("utf-8")
|
chunk = chunk if isinstance(chunk, str) else chunk.encode("utf-8")
|
||||||
self.assertLessEqual(len(chunk), max_byte_size)
|
self.assertLessEqual(len(chunk), max_byte_size)
|
||||||
|
|
||||||
|
def test_add_helper_meta_to_kwargs(self):
|
||||||
|
self.assertEqual(
|
||||||
|
actions._add_helper_meta_to_kwargs({}, "b"),
|
||||||
|
{"params": {"_client_meta": (("h", "b"),)}},
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
actions._add_helper_meta_to_kwargs({"params": {}}, "b"),
|
||||||
|
{"params": {"_client_meta": (("h", "b"),)}},
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
actions._add_helper_meta_to_kwargs({"params": {"key": "value"}}, "b"),
|
||||||
|
{"params": {"_client_meta": (("h", "b"),), "key": "value"}},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestExpandActions(TestCase):
|
class TestExpandActions(TestCase):
|
||||||
def test_string_actions_are_marked_as_simple_inserts(self):
|
def test_string_actions_are_marked_as_simple_inserts(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user