[7.x] Add support for the HTTP API compatibility header
Co-authored-by: Seth Michael Larson <[email protected]>
This commit is contained in:
co-authored by
Seth Michael Larson
parent
5ab523b703
commit
2c5f1d1569
@@ -60,7 +60,29 @@ To create an `SSLContext` object you only need to use one of cafile, capath or c
|
|||||||
* `capath` is the directory of a collection of CA's
|
* `capath` is the directory of a collection of CA's
|
||||||
* `cadata` is either an ASCII string of one or more PEM-encoded certificates or a bytes-like object of DER-encoded certificates.
|
* `cadata` is either an ASCII string of one or more PEM-encoded certificates or a bytes-like object of DER-encoded certificates.
|
||||||
|
|
||||||
Please note that the use of SSLContext is only available for Urllib3.
|
Please note that the use of SSLContext is only available for urllib3.
|
||||||
|
|
||||||
.. autoclass:: Urllib3HttpConnection
|
.. autoclass:: Urllib3HttpConnection
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
API Compatibility HTTP Header
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
The Python client can be configured to emit an HTTP header
|
||||||
|
``Accept: application/vnd.elasticsearch+json; compatible-with=7``
|
||||||
|
which signals to Elasticsearch that the client is requesting
|
||||||
|
``7.x`` version of request and response bodies. This allows for
|
||||||
|
upgrading from 7.x to 8.x version of Elasticsearch without upgrading
|
||||||
|
everything at once. Elasticsearch should be upgraded first after
|
||||||
|
the compatibility header is configured and clients should be upgraded
|
||||||
|
second.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from elasticsearch import Elasticsearch
|
||||||
|
|
||||||
|
client = Elasticsearch("http://...", headers={"accept": "application/vnd.elasticsearch+json; compatible-with=7"})
|
||||||
|
|
||||||
|
If you'd like to have the client emit the header without configuring ``headers`` you
|
||||||
|
can use the environment variable ``ELASTIC_CLIENT_APIVERSIONING=1``.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import binascii
|
|||||||
import gzip
|
import gzip
|
||||||
import io
|
import io
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
from platform import python_version
|
from platform import python_version
|
||||||
@@ -28,7 +29,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from .. import __versionstr__
|
from .. import __version__, __versionstr__
|
||||||
from ..exceptions import (
|
from ..exceptions import (
|
||||||
HTTP_EXCEPTIONS,
|
HTTP_EXCEPTIONS,
|
||||||
ElasticsearchWarning,
|
ElasticsearchWarning,
|
||||||
@@ -121,6 +122,13 @@ class Connection(object):
|
|||||||
if opaque_id:
|
if opaque_id:
|
||||||
self.headers["x-opaque-id"] = opaque_id
|
self.headers["x-opaque-id"] = opaque_id
|
||||||
|
|
||||||
|
if os.getenv("ELASTIC_CLIENT_APIVERSIONING") == "1":
|
||||||
|
self.headers.setdefault(
|
||||||
|
"accept",
|
||||||
|
"application/vnd.elasticsearch+json;compatible-with=%s"
|
||||||
|
% (str(__version__[0]),),
|
||||||
|
)
|
||||||
|
|
||||||
self.headers.setdefault("content-type", "application/json")
|
self.headers.setdefault("content-type", "application/json")
|
||||||
self.headers.setdefault("user-agent", self._get_default_user_agent())
|
self.headers.setdefault("user-agent", self._get_default_user_agent())
|
||||||
|
|
||||||
|
|||||||
@@ -154,8 +154,12 @@ class Deserializer(object):
|
|||||||
if not mimetype:
|
if not mimetype:
|
||||||
deserializer = self.default
|
deserializer = self.default
|
||||||
else:
|
else:
|
||||||
# split out charset
|
# split out 'charset' and 'compatible-width' options
|
||||||
mimetype, _, _ = mimetype.partition(";")
|
mimetype = mimetype.partition(";")[0].strip()
|
||||||
|
# Treat 'application/vnd.elasticsearch+json'
|
||||||
|
# as application/json for compatibility.
|
||||||
|
if mimetype == "application/vnd.elasticsearch+json":
|
||||||
|
mimetype = "application/json"
|
||||||
try:
|
try:
|
||||||
deserializer = self.serializers[mimetype]
|
deserializer = self.serializers[mimetype]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import gzip
|
import gzip
|
||||||
import io
|
import io
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import ssl
|
import ssl
|
||||||
import warnings
|
import warnings
|
||||||
@@ -171,6 +172,26 @@ class TestBaseConnection(TestCase):
|
|||||||
Connection(meta_header=1)
|
Connection(meta_header=1)
|
||||||
assert str(e.value) == "meta_header must be of type bool"
|
assert str(e.value) == "meta_header must be of type bool"
|
||||||
|
|
||||||
|
def test_compatibility_accept_header(self):
|
||||||
|
try:
|
||||||
|
conn = Connection()
|
||||||
|
assert "accept" not in conn.headers
|
||||||
|
|
||||||
|
os.environ["ELASTIC_CLIENT_APIVERSIONING"] = "0"
|
||||||
|
|
||||||
|
conn = Connection()
|
||||||
|
assert "accept" not in conn.headers
|
||||||
|
|
||||||
|
os.environ["ELASTIC_CLIENT_APIVERSIONING"] = "1"
|
||||||
|
|
||||||
|
conn = Connection()
|
||||||
|
assert (
|
||||||
|
conn.headers["accept"]
|
||||||
|
== "application/vnd.elasticsearch+json;compatible-with=8"
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
os.environ.pop("ELASTIC_CLIENT_APIVERSIONING")
|
||||||
|
|
||||||
|
|
||||||
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"{}"):
|
||||||
|
|||||||
@@ -185,6 +185,17 @@ class TestDeserializer(TestCase):
|
|||||||
self.de.loads('{"some":"data"}', "text/plain; charset=whatever"),
|
self.de.loads('{"some":"data"}', "text/plain; charset=whatever"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_deserialize_compatibility_header(self):
|
||||||
|
for content_type in (
|
||||||
|
"application/vnd.elasticsearch+json;compatible-with=7",
|
||||||
|
"application/vnd.elasticsearch+json; compatible-with=7",
|
||||||
|
"application/vnd.elasticsearch+json;compatible-with=8",
|
||||||
|
"application/vnd.elasticsearch+json; compatible-with=8",
|
||||||
|
):
|
||||||
|
self.assertEqual(
|
||||||
|
{"some": "data"}, self.de.loads('{"some":"data"}', content_type)
|
||||||
|
)
|
||||||
|
|
||||||
def test_raises_serialization_error_on_unknown_mimetype(self):
|
def test_raises_serialization_error_on_unknown_mimetype(self):
|
||||||
self.assertRaises(SerializationError, self.de.loads, "{}", "text/html")
|
self.assertRaises(SerializationError, self.de.loads, "{}", "text/html")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user