This is an automated email from the ASF dual-hosted git repository.
dstandish 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 783aa9cecb Add `section` query param in get config rest API (#30936)
783aa9cecb is described below
commit 783aa9cecbf47b4d0e5509d1919f644b9689b6b3
Author: Pankaj Singh <[email protected]>
AuthorDate: Fri Apr 28 22:54:24 2023 +0530
Add `section` query param in get config rest API (#30936)
---
airflow/api_connexion/endpoints/config_endpoint.py | 9 ++++-
airflow/api_connexion/openapi/v1.yaml | 7 ++++
airflow/www/static/js/types/api-generated.ts | 9 +++++
.../endpoints/test_config_endpoint.py | 38 ++++++++++++++++++++++
4 files changed, 62 insertions(+), 1 deletion(-)
diff --git a/airflow/api_connexion/endpoints/config_endpoint.py
b/airflow/api_connexion/endpoints/config_endpoint.py
index a9353234f0..3f3d5de4ce 100644
--- a/airflow/api_connexion/endpoints/config_endpoint.py
+++ b/airflow/api_connexion/endpoints/config_endpoint.py
@@ -24,6 +24,7 @@ from airflow.api_connexion import security
from airflow.api_connexion.exceptions import PermissionDenied
from airflow.api_connexion.schemas.config_schema import Config, ConfigOption,
ConfigSection, config_schema
from airflow.configuration import conf
+from airflow.exceptions import AirflowNotFoundException
from airflow.security import permissions
from airflow.settings import json
@@ -67,7 +68,7 @@ def _config_to_json(config: Config) -> str:
@security.requires_access([(permissions.ACTION_CAN_READ,
permissions.RESOURCE_CONFIG)])
-def get_config() -> Response:
+def get_config(*, section: str | None = None) -> Response:
"""Get current configuration."""
serializer = {
"text/plain": _config_to_text,
@@ -77,7 +78,13 @@ def get_config() -> Response:
if return_type not in serializer:
return Response(status=HTTPStatus.NOT_ACCEPTABLE)
elif conf.getboolean("webserver", "expose_config"):
+ if section and not conf.has_section(section):
+ raise AirflowNotFoundException(f"section={section} not found")
conf_dict = conf.as_dict(display_source=False, display_sensitive=True)
+ if section:
+ conf_section_value = conf_dict[section]
+ conf_dict.clear()
+ conf_dict[section] = conf_section_value
config = _conf_dict_to_config(conf_dict)
config_text = serializer[return_type](config)
return Response(config_text, headers={"Content-Type": return_type})
diff --git a/airflow/api_connexion/openapi/v1.yaml
b/airflow/api_connexion/openapi/v1.yaml
index bcd96442dd..f7edbe3c13 100644
--- a/airflow/api_connexion/openapi/v1.yaml
+++ b/airflow/api_connexion/openapi/v1.yaml
@@ -1955,6 +1955,13 @@ paths:
x-openapi-router-controller:
airflow.api_connexion.endpoints.config_endpoint
operationId: get_config
tags: [Config]
+ parameters:
+ - name: section
+ in: query
+ schema:
+ type: string
+ required: false
+ description: If given, only return config of this section.
responses:
'200':
description: Success.
diff --git a/airflow/www/static/js/types/api-generated.ts
b/airflow/www/static/js/types/api-generated.ts
index df89725669..2439b7a610 100644
--- a/airflow/www/static/js/types/api-generated.ts
+++ b/airflow/www/static/js/types/api-generated.ts
@@ -4176,6 +4176,12 @@ export interface operations {
};
};
get_config: {
+ parameters: {
+ query: {
+ /** If given, only return config of this section. */
+ section?: string;
+ };
+ };
responses: {
/** Success. */
200: {
@@ -4989,6 +4995,9 @@ export type GetDatasetVariables =
CamelCasedPropertiesDeep<
export type GetDatasetEventsVariables = CamelCasedPropertiesDeep<
operations["get_dataset_events"]["parameters"]["query"]
>;
+export type GetConfigVariables = CamelCasedPropertiesDeep<
+ operations["get_config"]["parameters"]["query"]
+>;
export type GetPluginsVariables = CamelCasedPropertiesDeep<
operations["get_plugins"]["parameters"]["query"]
>;
diff --git a/tests/api_connexion/endpoints/test_config_endpoint.py
b/tests/api_connexion/endpoints/test_config_endpoint.py
index b611e02786..940cc3764c 100644
--- a/tests/api_connexion/endpoints/test_config_endpoint.py
+++ b/tests/api_connexion/endpoints/test_config_endpoint.py
@@ -105,6 +105,44 @@ class TestGetConfig:
}
assert expected == response.json
+ @patch("airflow.api_connexion.endpoints.config_endpoint.conf.as_dict",
return_value=MOCK_CONF)
+ def test_should_respond_200_single_section_as_text_plain(self,
mock_as_dict):
+ response = self.client.get(
+ "/api/v1/config?section=smtp",
+ headers={"Accept": "text/plain"},
+ environ_overrides={"REMOTE_USER": "test"},
+ )
+ assert response.status_code == 200
+ expected = textwrap.dedent(
+ """\
+ [smtp]
+ smtp_host = localhost
+ smtp_mail_from = [email protected]
+ """
+ )
+ assert expected == response.data.decode()
+
+ @patch("airflow.api_connexion.endpoints.config_endpoint.conf.as_dict",
return_value=MOCK_CONF)
+ def test_should_respond_200_single_section_as_json(self, mock_as_dict):
+ response = self.client.get(
+ "/api/v1/config?section=smtp",
+ headers={"Accept": "application/json"},
+ environ_overrides={"REMOTE_USER": "test"},
+ )
+ assert response.status_code == 200
+ expected = {
+ "sections": [
+ {
+ "name": "smtp",
+ "options": [
+ {"key": "smtp_host", "value": "localhost"},
+ {"key": "smtp_mail_from", "value":
"[email protected]"},
+ ],
+ },
+ ]
+ }
+ assert expected == response.json
+
@patch("airflow.api_connexion.endpoints.config_endpoint.conf.as_dict",
return_value=MOCK_CONF)
def test_should_respond_406(self, mock_as_dict):
response = self.client.get(