[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
|
||||
* `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
|
||||
: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 io
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import warnings
|
||||
from platform import python_version
|
||||
@@ -28,7 +29,7 @@ try:
|
||||
except ImportError:
|
||||
import json
|
||||
|
||||
from .. import __versionstr__
|
||||
from .. import __version__, __versionstr__
|
||||
from ..exceptions import (
|
||||
HTTP_EXCEPTIONS,
|
||||
ElasticsearchWarning,
|
||||
@@ -121,6 +122,13 @@ class Connection(object):
|
||||
if 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("user-agent", self._get_default_user_agent())
|
||||
|
||||
|
||||
@@ -154,8 +154,12 @@ class Deserializer(object):
|
||||
if not mimetype:
|
||||
deserializer = self.default
|
||||
else:
|
||||
# split out charset
|
||||
mimetype, _, _ = mimetype.partition(";")
|
||||
# split out 'charset' and 'compatible-width' options
|
||||
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:
|
||||
deserializer = self.serializers[mimetype]
|
||||
except KeyError:
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import gzip
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import ssl
|
||||
import warnings
|
||||
@@ -171,6 +172,26 @@ class TestBaseConnection(TestCase):
|
||||
Connection(meta_header=1)
|
||||
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):
|
||||
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"),
|
||||
)
|
||||
|
||||
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):
|
||||
self.assertRaises(SerializationError, self.de.loads, "{}", "text/html")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user