This is an automated email from the ASF dual-hosted git repository.

ephraimanierobi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new e89a7eeea6 Use custom validator for OpenAPI request body (#30596)
e89a7eeea6 is described below

commit e89a7eeea6a7a5a5a30a3f3cf86dfabf7c343412
Author: Tzu-ping Chung <[email protected]>
AuthorDate: Wed Apr 12 20:40:05 2023 +0800

    Use custom validator for OpenAPI request body (#30596)
    
    * Use custom validator for OpenAPI request body
    
    The default error message for an empty request body from Connexion
    is quite unhelpful (taken directly from JSONSchema). This custom
    validator emits a more helpful message for this particular context.
    
    * Add test for custom request body validator
    
    Co-Authored-By: maahir22 <[email protected]>
    
    ---------
    
    Co-authored-by: maahir22 <[email protected]>
---
 airflow/www/extensions/init_views.py                    | 17 +++++++++++++++++
 .../endpoints/test_task_instance_endpoint.py            |  8 ++++++++
 2 files changed, 25 insertions(+)

diff --git a/airflow/www/extensions/init_views.py 
b/airflow/www/extensions/init_views.py
index 41408942e4..d3dc3b7c62 100644
--- a/airflow/www/extensions/init_views.py
+++ b/airflow/www/extensions/init_views.py
@@ -21,6 +21,8 @@ import warnings
 from os import path
 
 from connexion import FlaskApi, ProblemException, Resolver
+from connexion.decorators.validation import RequestBodyValidator
+from connexion.exceptions import BadRequestProblem
 from flask import Flask, request
 
 from airflow.api_connexion.exceptions import common_error_handler
@@ -209,6 +211,20 @@ class _LazyResolver(Resolver):
         return _LazyResolution(self.resolve_function_from_operation_id, 
operation_id)
 
 
+class _CustomErrorRequestBodyValidator(RequestBodyValidator):
+    """Custom request body validator that overrides error messages.
+
+    By default, Connextion emits a very generic *None is not of type 'object'*
+    error when receiving an empty request body (with the view specifying the
+    body as non-nullable). We overrides it to provide a more useful message.
+    """
+
+    def validate_schema(self, data, url):
+        if not self.is_null_value_valid and data is None:
+            raise BadRequestProblem(detail="Request body must not be empty")
+        return super().validate_schema(data, url)
+
+
 def init_api_connexion(app: Flask) -> None:
     """Initialize Stable API"""
     base_path = "/api/v1"
@@ -245,6 +261,7 @@ def init_api_connexion(app: Flask) -> None:
         },
         strict_validation=True,
         validate_responses=True,
+        validator_map={"body": _CustomErrorRequestBodyValidator},
     ).blueprint
     api_bp.after_request(set_cors_headers_on_response)
 
diff --git a/tests/api_connexion/endpoints/test_task_instance_endpoint.py 
b/tests/api_connexion/endpoints/test_task_instance_endpoint.py
index ac2b4b694d..9f1f74df55 100644
--- a/tests/api_connexion/endpoints/test_task_instance_endpoint.py
+++ b/tests/api_connexion/endpoints/test_task_instance_endpoint.py
@@ -875,6 +875,14 @@ class TestGetTaskInstancesBatch(TestTaskInstanceEndpoint):
         )
         assert response.status_code == 403
 
+    def test_should_raise_400_for_no_json(self):
+        response = self.client.post(
+            "/api/v1/dags/~/dagRuns/~/taskInstances/list",
+            environ_overrides={"REMOTE_USER": "test"},
+        )
+        assert response.status_code == 400
+        assert response.json["detail"] == "Request body must not be empty"
+
     @pytest.mark.parametrize(
         "payload, expected",
         [

Reply via email to