Repository: opensearch-project/opensearch-py
Pull request #100 (2021-12-20 17:45:18)

Add default `needs-triage` label to issue templates
### Description
Currently, there is no convenient way to get a list of untriaged issues. This PR adds `needs-triage` label to newly created issues.
 
### Issues Resolved
 
### 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/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md b/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md
index 605be98b..683466b5 100644
--- a/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md
@@ -2,7 +2,7 @@
 name: 🐛 Bug report
 about: Create a report to help us improve
 title: '[BUG]'
-labels: 'bug, untriaged'
+labels: 'bug, needs-triage
 assignees: ''
 ---
 
@@ -28,4 +28,4 @@ A clear and concise description of what you expected to happen.
 If applicable, add screenshots to help explain your problem.
 
 **Do you have any additional context?**
-Add any other context about the problem.
\ No newline at end of file
+Add any other context about the problem.
diff --git a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST_TEMPLATE.md b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST_TEMPLATE.md
index 19b01d9f..addf30d1 100644
--- a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST_TEMPLATE.md
@@ -2,7 +2,7 @@
 name: 🎆 Feature request
 about: Request a feature in this project
 title: '[FEATURE]'
-labels: enhancement
+labels: enhancement, needs-triage
 assignees: ''
 ---
 **Is your feature request related to a problem?**
@@ -15,4 +15,4 @@ A clear and concise description of what you want to happen.
 A clear and concise description of any alternative solutions or features you've considered.
 
 **Do you have any additional context?**
-Add any other context or screenshots about the feature request here.
\ No newline at end of file
+Add any other context or screenshots about the feature request here.
diff --git a/.github/ISSUE_TEMPLATE/PROPOSAL_TEMPLATE.md b/.github/ISSUE_TEMPLATE/PROPOSAL_TEMPLATE.md
index 1f891242..e8fe38ca 100644
--- a/.github/ISSUE_TEMPLATE/PROPOSAL_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE/PROPOSAL_TEMPLATE.md
@@ -2,7 +2,7 @@
 name: 💭 Proposal
 about: Suggest an idea for a specific feature you wish to propose to the community for comment
 title: '[PROPOSAL]'
-labels: proposal
+labels: proposal, needs-triage
 assignees: ''
 ---
 ## What kind of business use case are you trying to solve? What are your requirements?
diff --git a/opensearchpy/connection/base.py b/opensearchpy/connection/base.py
index 199f122f..d8365ed8 100644
--- a/opensearchpy/connection/base.py
+++ b/opensearchpy/connection/base.py
@@ -314,12 +314,17 @@ class Connection(object):
         if response is not None:
             logger.debug("< %s", response)
 
-    def _raise_error(self, status_code, raw_data):
+    def _raise_error(self, status_code, raw_data, content_type=None):
         """Locate appropriate exception and raise it."""
         error_message = raw_data
         additional_info = None
         try:
-            if raw_data:
+            content_type = (
+                "text/plain"
+                if content_type is None
+                else content_type.split(";")[0].strip()
+            )
+            if raw_data and content_type == "application/json":
                 additional_info = json.loads(raw_data)
                 error_message = additional_info.get("error", error_message)
                 if isinstance(error_message, dict) and "type" in error_message:
diff --git a/opensearchpy/connection/base.pyi b/opensearchpy/connection/base.pyi
index 8afbfb28..b25d9629 100644
--- a/opensearchpy/connection/base.pyi
+++ b/opensearchpy/connection/base.pyi
@@ -112,6 +112,8 @@ class Connection(object):
         response: Optional[str] = ...,
         exception: Optional[Exception] = ...,
     ) -> None: ...
-    def _raise_error(self, status_code: int, raw_data: str) -> NoReturn: ...
+    def _raise_error(
+        self, status_code: int, raw_data: str, content_type: Optional[str]
+    ) -> NoReturn: ...
     def _get_default_user_agent(self) -> str: ...
     def _get_api_key_header_val(self, api_key: Any) -> str: ...
diff --git a/opensearchpy/connection/http_requests.py b/opensearchpy/connection/http_requests.py
index 4ddf2ffc..9b8952c4 100644
--- a/opensearchpy/connection/http_requests.py
+++ b/opensearchpy/connection/http_requests.py
@@ -207,7 +207,11 @@ class RequestsHttpConnection(Connection):
                 response.status_code,
                 raw_data,
             )
-            self._raise_error(response.status_code, raw_data)
+            self._raise_error(
+                response.status_code,
+                raw_data,
+                response.headers.get("Content-Type"),
+            )
 
         self.log_request_success(
             method,
diff --git a/opensearchpy/connection/http_urllib3.py b/opensearchpy/connection/http_urllib3.py
index 51410350..f3bb698e 100644
--- a/opensearchpy/connection/http_urllib3.py
+++ b/opensearchpy/connection/http_urllib3.py
@@ -279,7 +279,11 @@ class Urllib3HttpConnection(Connection):
             self.log_request_fail(
                 method, full_url, url, orig_body, duration, response.status, raw_data
             )
-            self._raise_error(response.status, raw_data)
+            self._raise_error(
+                response.status,
+                raw_data,
+                self.get_response_headers(response).get("content-type"),
+            )
 
         self.log_request_success(
             method, full_url, url, orig_body, response.status, raw_data, duration
@@ -287,6 +291,9 @@ class Urllib3HttpConnection(Connection):
 
         return response.status, response.getheaders(), raw_data
 
+    def get_response_headers(self, response):
+        return {header.lower(): value for header, value in response.headers.items()}
+
     def close(self):
         """
         Explicitly closes connection


Test patch:
diff --git a/test_opensearchpy/test_connection.py b/test_opensearchpy/test_connection.py
index bca7d3e7..48e2ffb5 100644
--- a/test_opensearchpy/test_connection.py
+++ b/test_opensearchpy/test_connection.py
@@ -31,10 +31,12 @@ import json
 import os
 import re
 import ssl
+import unittest
 import warnings
 from platform import python_version
 
 import pytest
+import six
 import urllib3
 from mock import Mock, patch
 from requests.auth import AuthBase
@@ -163,6 +165,19 @@ class TestBaseConnection(TestCase):
 
         self.assertEqual([str(w.message) for w in warn], ["warning", "folded"])
 
+    @unittest.skipIf(six.PY2, "not compatible with python2")
+    def test_raises_errors(self):
+        con = Connection()
+        with self.assertLogs("opensearch") as captured, self.assertRaises(
+            NotFoundError
+        ):
+            con._raise_error(404, "Not found", "application/json")
+        self.assertEqual(len(captured.output), 1)
+
+        # NB: this should assertNoLogs() but that method is not available until python3.10
+        with self.assertRaises(NotFoundError):
+            con._raise_error(404, "Not found", "text/plain; charset=UTF-8")
+
     def test_ipv6_host_and_port(self):
         for kwargs, expected_host in [
             ({"host": "::1"}, "http://[::1]:9200"),