Repository: opensearch-project/opensearch-py
Pull request #57 (2021-08-26 13:47:42)

Add developer guide
Signed-off-by: Rushi Agrawal <rushi.agr@gmail.com>

### Description
Add developer guide
 
### Issues Resolved
Progress on #10 
 
### Check List
- [x] Commits are signed per the DCO using --signoff 

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check [here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).



Patch:
diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md
new file mode 100644
index 00000000..827ddd08
--- /dev/null
+++ b/DEVELOPER_GUIDE.md
@@ -0,0 +1,22 @@
+Run the linter and test suite to ensure your changes do not break existing code:
+
+```
+# Install Nox for task management
+$ python -m pip install nox
+
+# Auto-format and lint your changes
+$ nox -rs format
+
+# Run the test suite
+$ nox -rs test
+```
+
+To run the integration tests locally, run:
+
+```
+./.ci/run-tests opensearch true
+```
+
+The first argument, `opensearch` tells the server type to run integration test against. Valid values are `opensearch` and `opendistro`. The second argument tells whether to run server with security plugin enabled or not.
+
+Note that integration tests require docker to be installed and running, and downloads quite a bit of data from over the internet and hence take few minutes to complete.
diff --git a/README.md b/README.md
index deb2411a..68e4661b 100644
--- a/README.md
+++ b/README.md
@@ -21,37 +21,119 @@ OpenSearch Python Client
 
 This is the low-level client. A high-level Python client is in the works, and will be available soon.
 
-## Example use
+## Setup
 
-```python
+To add the client to your project, install it using [pip](https://pip.pypa.io/):
 
->>> from datetime import datetime
->>> from opensearch import OpenSearch
+```bash
+pip install git+https://github.com/opensearch-project/opensearch-py.git
+```
 
-# by default we connect to localhost:9200
->>> client = OpenSearch()
+Then import it like any other module:
 
-# create an index in OpenSearch, ignore status code 400 (index already exists)
->>> client.indices.create(index='my-index', ignore=400)
-{'acknowledged': True, 'shards_acknowledged': True, 'index': 'my-index'}
+```python
+from opensearch import OpenSearch
+```
 
-# datetimes will be serialized
->>> client.index(index="my-index", id=42, body={"any": "data", "timestamp": datetime.now()})
-{'_index': 'my-index',
-    '_type': '_doc',
-    '_id': '42',
-    '_version': 1,
-    'result': 'created',
-    '_shards': {'total': 2, 'successful': 1, 'failed': 0},
-    '_seq_no': 0,
-    '_primary_term': 1}
+If you prefer to add the client manually or just want to examine the source code, see [opensearch-py on GitHub](https://github.com/opensearch-project/opensearch-py).
 
-# but not deserialized
->>> client.get(index="my-index", id=42)['_source']
-{'any': 'data', 'timestamp': '2019-05-17T17:28:10.329598'}
 
-```
+## Sample code
 
+```python
+from opensearch import OpenSearch
+
+host = 'localhost'
+port = 9200
+auth = ('admin', 'admin') # For testing only. Don't store credentials in code.
+ca_certs_path = '/full/path/to/root-ca.pem' # Provide a CA bundle if you use intermediate CAs with your root CA.
+
+# Optional client certificates if you don't want to use HTTP basic authentication.
+# client_cert_path = '/full/path/to/client.pem'
+# client_key_path = '/full/path/to/client-key.pem'
+
+# Create the client with SSL/TLS enabled, but hostname verification disabled.
+client = OpenSearch(
+    hosts = [{'host': host, 'port': port}],
+    http_compress = True, # enables gzip compression for request bodies
+    http_auth = auth,
+    # client_cert = client_cert_path,
+    # client_key = client_key_path,
+    use_ssl = True,
+    verify_certs = True,
+    ssl_assert_hostname = False,
+    ssl_show_warn = False,
+    ca_certs = ca_certs_path
+)
+
+# Create an index with non-default settings.
+index_name = 'python-test-index3'
+index_body = {
+  'settings': {
+    'index': {
+      'number_of_shards': 4
+    }
+  }
+}
+
+response = client.indices.create(index_name, body=index_body)
+print('\nCreating index:')
+print(response)
+
+# Add a document to the index.
+document = {
+  'title': 'Moneyball',
+  'director': 'Bennett Miller',
+  'year': '2011'
+}
+id = '1'
+
+response = client.index(
+    index = index_name,
+    body = document,
+    id = id,
+    refresh = True
+)
+
+print('\nAdding document:')
+print(response)
+
+# Search for the document.
+q = 'miller'
+query = {
+  'size': 5,
+  'query': {
+    'multi_match': {
+      'query': q,
+      'fields': ['title^2', 'director']
+    }
+  }
+}
+
+response = client.search(
+    body = query,
+    index = index_name
+)
+print('\nSearch results:')
+print(response)
+
+# Delete the document.
+response = client.delete(
+    index = index_name,
+    id = id
+)
+
+print('\nDeleting document:')
+print(response)
+
+# Delete the index.
+response = client.indices.delete(
+    index = index_name
+)
+
+print('\nDeleting index:')
+print(response)
+```
 
 ## Project Resources
 
diff --git a/opensearch/connection/base.py b/opensearch/connection/base.py
index 05d81c71..b8c27237 100644
--- a/opensearch/connection/base.py
+++ b/opensearch/connection/base.py
@@ -28,6 +28,7 @@ import binascii
 import gzip
 import io
 import logging
+import os
 import re
 import warnings
 from platform import python_version
@@ -127,6 +128,11 @@ 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=7"
+            )
+
         self.headers.setdefault("content-type", "application/json")
         self.headers.setdefault("user-agent", self._get_default_user_agent())
 
diff --git a/opensearch/serializer.py b/opensearch/serializer.py
index fcc9b146..d7180e8e 100644
--- a/opensearch/serializer.py
+++ b/opensearch/serializer.py
@@ -163,6 +163,11 @@ class Deserializer(object):
         if not mimetype:
             deserializer = self.default
         else:
+            # Treat 'application/vnd.elasticsearch+json'
+            # as application/json for compatibility.
+            if mimetype == "application/vnd.elasticsearch+json":
+                mimetype = "application/json"
+
             # split out charset
             mimetype, _, _ = mimetype.partition(";")
             try:


Test patch:
diff --git a/test_opensearch/test_connection.py b/test_opensearch/test_connection.py
index c02a7183..e296a29b 100644
--- a/test_opensearch/test_connection.py
+++ b/test_opensearch/test_connection.py
@@ -28,6 +28,7 @@
 import gzip
 import io
 import json
+import os
 import re
 import ssl
 import warnings
@@ -173,6 +174,26 @@ class TestBaseConnection(TestCase):
             conn = Connection(**kwargs)
             assert conn.host == expected_host
 
+    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=7"
+            )
+        finally:
+            os.environ.pop("ELASTIC_CLIENT_APIVERSIONING")
+
 
 class TestUrllib3Connection(TestCase):
     def _get_mock_connection(self, connection_params={}, response_body=b"{}"):