You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by bu...@apache.org on 2025/06/11 21:43:57 UTC
(airflow) branch main updated: airflowctl: convert to `BulkResponse` in variables.import (#51514)
This is an automated email from the ASF dual-hosted git repository.
bugraoz 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 8dd60798a76 airflowctl: convert to `BulkResponse` in variables.import (#51514)
8dd60798a76 is described below
commit 8dd60798a762bd4676f4292ce97484c61cc20179
Author: jj.lee <63...@users.noreply.github.com>
AuthorDate: Thu Jun 12 06:43:52 2025 +0900
airflowctl: convert to `BulkResponse` in variables.import (#51514)
* to BulkResponse in variables.bulk
* modified import cmd
* unit tests
* fix operation bulktest
---
airflow-ctl/src/airflowctl/api/operations.py | 5 ++-
.../airflowctl/ctl/commands/variable_command.py | 18 ++++++---
.../tests/airflow_ctl/api/test_operations.py | 8 ++--
.../ctl/commands/test_variable_command.py | 44 ++++++++++++++++++++--
4 files changed, 60 insertions(+), 15 deletions(-)
diff --git a/airflow-ctl/src/airflowctl/api/operations.py b/airflow-ctl/src/airflowctl/api/operations.py
index 00f76013482..7350b1aa2d6 100644
--- a/airflow-ctl/src/airflowctl/api/operations.py
+++ b/airflow-ctl/src/airflowctl/api/operations.py
@@ -37,6 +37,7 @@ from airflowctl.api.datamodels.generated import (
BulkBodyConnectionBody,
BulkBodyPoolBody,
BulkBodyVariableBody,
+ BulkResponse,
Config,
ConnectionBody,
ConnectionCollectionResponse,
@@ -672,11 +673,11 @@ class VariablesOperations(BaseOperations):
except ServerResponseError as e:
raise e
- def bulk(self, variables: BulkBodyVariableBody) -> BulkActionResponse | ServerResponseError:
+ def bulk(self, variables: BulkBodyVariableBody) -> BulkResponse | ServerResponseError:
"""CRUD multiple variables."""
try:
self.response = self.client.patch("variables", json=variables.model_dump())
- return BulkActionResponse.model_validate_json(self.response.content)
+ return BulkResponse.model_validate_json(self.response.content)
except ServerResponseError as e:
raise e
diff --git a/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py b/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
index 1c31828a372..d5ffed31d8c 100644
--- a/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
+++ b/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
@@ -23,9 +23,9 @@ from pathlib import Path
import rich
-from airflow.api_fastapi.core_api.datamodels.common import BulkActionOnExistence
from airflowctl.api.client import NEW_API_CLIENT, ClientKind, provide_api_client
from airflowctl.api.datamodels.generated import (
+ BulkActionOnExistence,
BulkBodyVariableBody,
BulkCreateActionVariableBody,
VariableBody,
@@ -33,9 +33,11 @@ from airflowctl.api.datamodels.generated import (
@provide_api_client(kind=ClientKind.CLI)
-def import_(args, api_client=NEW_API_CLIENT):
+def import_(args, api_client=NEW_API_CLIENT) -> list[str]:
"""Import variables from a given file."""
- success_message = "[green]Import successful! success: {success}, errors: {errors}[/green]"
+ success_message = "[green]Import successful! success: {success}[/green]"
+ errors_message = "[red]Import failed! errors: {errors}[/red]"
+
if not os.path.exists(args.file):
rich.print(f"[red]Missing variable file: {args.file}")
sys.exit(1)
@@ -71,12 +73,16 @@ def import_(args, api_client=NEW_API_CLIENT):
]
)
result = api_client.variables.bulk(variables=bulk_body)
- rich.print(success_message.format(success=result.success, errors=result.errors))
- return result.success, result.errors
+ if result.create.errors:
+ rich.print(errors_message.format(errors=result.create.errors))
+ sys.exit(1)
+
+ rich.print(success_message.format(success=result.create.success))
+ return result.create.success
@provide_api_client(kind=ClientKind.CLI)
-def export(args, api_client=NEW_API_CLIENT):
+def export(args, api_client=NEW_API_CLIENT) -> None:
"""Export all the variables to the file."""
success_message = "[green]Export successful! {total_entries} variable(s) to {file}[/green]"
var_dict = {}
diff --git a/airflow-ctl/tests/airflow_ctl/api/test_operations.py b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
index 7cc058c1952..a727783bbba 100644
--- a/airflow-ctl/tests/airflow_ctl/api/test_operations.py
+++ b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
@@ -44,6 +44,7 @@ from airflowctl.api.datamodels.generated import (
BulkCreateActionConnectionBody,
BulkCreateActionPoolBody,
BulkCreateActionVariableBody,
+ BulkResponse,
Config,
ConfigOption,
ConfigSection,
@@ -1054,9 +1055,10 @@ class TestVariablesOperations:
)
]
)
- variable_bulk_response = BulkActionResponse(
- success=[key],
- errors=[],
+ variable_bulk_response = BulkResponse(
+ create=BulkActionResponse(success=[key], errors=[]),
+ update=None,
+ delete=None,
)
def test_get(self):
diff --git a/airflow-ctl/tests/airflow_ctl/ctl/commands/test_variable_command.py b/airflow-ctl/tests/airflow_ctl/ctl/commands/test_variable_command.py
index 72116b73f58..e0c9ce16d8d 100644
--- a/airflow-ctl/tests/airflow_ctl/ctl/commands/test_variable_command.py
+++ b/airflow-ctl/tests/airflow_ctl/ctl/commands/test_variable_command.py
@@ -19,9 +19,12 @@ from __future__ import annotations
import json
import os
+import pytest
+
from airflowctl.api.client import ClientKind
from airflowctl.api.datamodels.generated import (
BulkActionResponse,
+ BulkResponse,
VariableCollectionResponse,
VariableResponse,
)
@@ -46,12 +49,24 @@ class TestCliVariableCommands:
],
total_entries=1,
)
- bulk_action_response = BulkActionResponse(success=[key], errors=[])
+ bulk_response_success = BulkResponse(
+ create=BulkActionResponse(success=[key], errors=[]), update=None, delete=None
+ )
+ bulk_response_error = BulkResponse(
+ create=BulkActionResponse(
+ success=[],
+ errors=[
+ {"error": f"The variables with these keys: {{'{key}'}} already exist.", "status_code": 409}
+ ],
+ ),
+ update=None,
+ delete=None,
+ )
- def test_import(self, api_client_maker, tmp_path, monkeypatch):
+ def test_import_success(self, api_client_maker, tmp_path, monkeypatch):
api_client = api_client_maker(
path="/api/v2/variables",
- response_json=self.bulk_action_response.model_dump(),
+ response_json=self.bulk_response_success.model_dump(),
expected_http_status_code=200,
kind=ClientKind.CLI,
)
@@ -67,7 +82,28 @@ class TestCliVariableCommands:
self.parser.parse_args(["variables", "import", expected_json_path.as_posix()]),
api_client=api_client,
)
- assert response == ([self.key], [])
+ assert response == [self.key]
+
+ def test_import_error(self, api_client_maker, tmp_path, monkeypatch):
+ api_client = api_client_maker(
+ path="/api/v2/variables",
+ response_json=self.bulk_response_error.model_dump(),
+ expected_http_status_code=200,
+ kind=ClientKind.CLI,
+ )
+
+ monkeypatch.chdir(tmp_path)
+ expected_json_path = tmp_path / self.export_file_name
+ variable_file = {
+ self.key: self.value,
+ }
+
+ expected_json_path.write_text(json.dumps(variable_file))
+ with pytest.raises(SystemExit):
+ variable_command.import_(
+ self.parser.parse_args(["variables", "import", expected_json_path.as_posix()]),
+ api_client=api_client,
+ )
def test_export(self, api_client_maker, tmp_path, monkeypatch):
api_client = api_client_maker(