This is an automated email from the ASF dual-hosted git repository.
potiuk 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 7bf53395f2 Make connection id validation consistent across interface
(#31282)
7bf53395f2 is described below
commit 7bf53395f2802b9952b0e90ffe548f70c50907ad
Author: Pankaj Singh <[email protected]>
AuthorDate: Thu May 18 13:10:34 2023 +0530
Make connection id validation consistent across interface (#31282)
* Make connection id validation consistent across interface
recently, we added the connection id validation
if the user creates a connection from UI but
this improvement is missing in CLI and API.
This PR make sure that connection id validation is consistent
across interfaces like CLI, API, and UI.
---
airflow/api_connexion/endpoints/connection_endpoint.py | 5 +++++
airflow/cli/commands/connection_command.py | 14 +++++++++++++-
tests/api_connexion/endpoints/test_connection_endpoint.py | 14 ++++++++++++++
tests/cli/commands/test_connection_command.py | 10 ++++++++++
4 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/airflow/api_connexion/endpoints/connection_endpoint.py
b/airflow/api_connexion/endpoints/connection_endpoint.py
index 7e2a7dd8da..641c3288d3 100644
--- a/airflow/api_connexion/endpoints/connection_endpoint.py
+++ b/airflow/api_connexion/endpoints/connection_endpoint.py
@@ -39,6 +39,7 @@ from airflow.api_connexion.types import APIResponse,
UpdateMask
from airflow.models import Connection
from airflow.secrets.environment_variables import CONN_ENV_PREFIX
from airflow.security import permissions
+from airflow.utils import helpers
from airflow.utils.log.action_logger import action_event_from_permission
from airflow.utils.session import NEW_SESSION, provide_session
from airflow.utils.strings import get_random_string
@@ -157,6 +158,10 @@ def post_connection(*, session: Session = NEW_SESSION) ->
APIResponse:
except ValidationError as err:
raise BadRequest(detail=str(err.messages))
conn_id = data["conn_id"]
+ try:
+ helpers.validate_key(conn_id, max_length=200)
+ except Exception as e:
+ raise BadRequest(detail=str(e))
query = session.query(Connection)
connection = query.filter_by(conn_id=conn_id).first()
if not connection:
diff --git a/airflow/cli/commands/connection_command.py
b/airflow/cli/commands/connection_command.py
index dc8c1792e3..6d137bc896 100644
--- a/airflow/cli/commands/connection_command.py
+++ b/airflow/cli/commands/connection_command.py
@@ -35,7 +35,7 @@ from airflow.hooks.base import BaseHook
from airflow.models import Connection
from airflow.providers_manager import ProvidersManager
from airflow.secrets.local_filesystem import load_connections_dict
-from airflow.utils import cli as cli_utils, yaml
+from airflow.utils import cli as cli_utils, helpers, yaml
from airflow.utils.cli import suppress_logs_and_warning
from airflow.utils.session import create_session
@@ -203,6 +203,12 @@ def connections_add(args):
has_json = bool(args.conn_json)
has_type = bool(args.conn_type)
+ # Validate connection-id
+ try:
+ helpers.validate_key(args.conn_id, max_length=200)
+ except Exception as e:
+ raise SystemExit(f"Could not create connection. {e}")
+
if not has_type and not (has_json or has_uri):
raise SystemExit("Must supply either conn-uri or conn-json if not
supplying conn-type")
@@ -313,6 +319,12 @@ def _import_helper(file_path: str, overwrite: bool) ->
None:
connections_dict = load_connections_dict(file_path)
with create_session() as session:
for conn_id, conn in connections_dict.items():
+ try:
+ helpers.validate_key(conn_id, max_length=200)
+ except Exception as e:
+ print(f"Could not import connection. {e}")
+ continue
+
existing_conn_id =
session.query(Connection.id).filter(Connection.conn_id == conn_id).scalar()
if existing_conn_id is not None:
if not overwrite:
diff --git a/tests/api_connexion/endpoints/test_connection_endpoint.py
b/tests/api_connexion/endpoints/test_connection_endpoint.py
index a7df127d90..bce7150f8b 100644
--- a/tests/api_connexion/endpoints/test_connection_endpoint.py
+++ b/tests/api_connexion/endpoints/test_connection_endpoint.py
@@ -562,6 +562,20 @@ class TestPostConnection(TestConnectionEndpoint):
"type": EXCEPTIONS_LINK_MAP[400],
}
+ def test_post_should_respond_400_for_invalid_conn_id(self):
+ payload = {"connection_id": "****", "conn_type": "test_type"}
+ response = self.client.post(
+ "/api/v1/connections", json=payload,
environ_overrides={"REMOTE_USER": "test"}
+ )
+ assert response.status_code == 400
+ assert response.json == {
+ "detail": "The key '****' has to be made of "
+ "alphanumeric characters, dashes, dots and underscores
exclusively",
+ "status": 400,
+ "title": "Bad Request",
+ "type": EXCEPTIONS_LINK_MAP[400],
+ }
+
def test_post_should_respond_409_already_exist(self):
payload = {"connection_id": "test-connection-id", "conn_type":
"test_type"}
response = self.client.post(
diff --git a/tests/cli/commands/test_connection_command.py
b/tests/cli/commands/test_connection_command.py
index 692039fb9e..42f6fc8cc2 100644
--- a/tests/cli/commands/test_connection_command.py
+++ b/tests/cli/commands/test_connection_command.py
@@ -635,6 +635,16 @@ class TestCliAddConnections:
)
)
+ def test_cli_connections_add_invalid_conn_id(self):
+ with pytest.raises(SystemExit) as e:
+ connection_command.connections_add(
+ self.parser.parse_args(["connections", "add", "Test$",
f"--conn-uri={TEST_URL}"])
+ )
+ assert (
+ e.value.args[0] == "Could not create connection. The key 'Test$'
has to be made of "
+ "alphanumeric characters, dashes, dots and underscores exclusively"
+ )
+
class TestCliDeleteConnections:
parser = cli_parser.get_parser()