This is an automated email from the ASF dual-hosted git repository.
pierrejeambrun 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 ddae5a548a5 Add variable key search support (#44794)
ddae5a548a5 is described below
commit ddae5a548a5d7b6f3ebdcbe858c79eb467335c36
Author: Shubham Raj <[email protected]>
AuthorDate: Tue Dec 10 20:40:59 2024 +0530
Add variable key search support (#44794)
* add primary search
* added tests
* fix tests
* fix test
---
airflow/api_fastapi/common/parameters.py | 6 +++
.../api_fastapi/core_api/openapi/v1-generated.yaml | 8 +++
.../core_api/routes/public/variables.py | 9 +++-
airflow/ui/openapi-gen/queries/common.ts | 4 +-
airflow/ui/openapi-gen/queries/prefetch.ts | 12 ++++-
airflow/ui/openapi-gen/queries/queries.ts | 12 ++++-
airflow/ui/openapi-gen/queries/suspense.ts | 12 ++++-
airflow/ui/openapi-gen/requests/services.gen.ts | 2 +
airflow/ui/openapi-gen/requests/types.gen.ts | 1 +
.../core_api/routes/public/test_variables.py | 61 ++++++++++++++++++----
10 files changed, 111 insertions(+), 16 deletions(-)
diff --git a/airflow/api_fastapi/common/parameters.py
b/airflow/api_fastapi/common/parameters.py
index dbaf4581ac1..423de808551 100644
--- a/airflow/api_fastapi/common/parameters.py
+++ b/airflow/api_fastapi/common/parameters.py
@@ -49,6 +49,7 @@ from airflow.models.asset import (
from airflow.models.dag import DagModel, DagTag
from airflow.models.dagrun import DagRun
from airflow.models.taskinstance import TaskInstance
+from airflow.models.variable import Variable
from airflow.typing_compat import Self
from airflow.utils import timezone
from airflow.utils.state import DagRunState, TaskInstanceState
@@ -583,3 +584,8 @@ QueryAssetAliasNamePatternSearch = Annotated[
QueryAssetDagIdPatternSearch = Annotated[
_DagIdAssetReferenceFilter, Depends(_DagIdAssetReferenceFilter().depends)
]
+
+# Variables
+QueryVariableKeyPatternSearch = Annotated[
+ _SearchParam, Depends(search_param_factory(Variable.key,
"variable_key_pattern"))
+]
diff --git a/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
b/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
index bd73d3a306a..3b6d55c9313 100644
--- a/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
+++ b/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
@@ -5549,6 +5549,14 @@ paths:
type: string
default: id
title: Order By
+ - name: variable_key_pattern
+ in: query
+ required: false
+ schema:
+ anyOf:
+ - type: string
+ - type: 'null'
+ title: Variable Key Pattern
responses:
'200':
description: Successful Response
diff --git a/airflow/api_fastapi/core_api/routes/public/variables.py
b/airflow/api_fastapi/core_api/routes/public/variables.py
index 0e1cebbe0a2..1b970444f31 100644
--- a/airflow/api_fastapi/core_api/routes/public/variables.py
+++ b/airflow/api_fastapi/core_api/routes/public/variables.py
@@ -22,7 +22,12 @@ from fastapi import Depends, HTTPException, Query, status
from sqlalchemy import select
from airflow.api_fastapi.common.db.common import SessionDep, paginated_select
-from airflow.api_fastapi.common.parameters import QueryLimit, QueryOffset,
SortParam
+from airflow.api_fastapi.common.parameters import (
+ QueryLimit,
+ QueryOffset,
+ QueryVariableKeyPatternSearch,
+ SortParam,
+)
from airflow.api_fastapi.common.router import AirflowRouter
from airflow.api_fastapi.core_api.datamodels.variables import (
VariableBody,
@@ -86,10 +91,12 @@ def get_variables(
),
],
session: SessionDep,
+ varaible_key_pattern: QueryVariableKeyPatternSearch,
) -> VariableCollectionResponse:
"""Get all Variables entries."""
variable_select, total_entries = paginated_select(
statement=select(Variable),
+ filters=[varaible_key_pattern],
order_by=order_by,
offset=offset,
limit=limit,
diff --git a/airflow/ui/openapi-gen/queries/common.ts
b/airflow/ui/openapi-gen/queries/common.ts
index d1a5f20511d..b759c0f2f23 100644
--- a/airflow/ui/openapi-gen/queries/common.ts
+++ b/airflow/ui/openapi-gen/queries/common.ts
@@ -1597,15 +1597,17 @@ export const UseVariableServiceGetVariablesKeyFn = (
limit,
offset,
orderBy,
+ variableKeyPattern,
}: {
limit?: number;
offset?: number;
orderBy?: string;
+ variableKeyPattern?: string;
} = {},
queryKey?: Array<unknown>,
) => [
useVariableServiceGetVariablesKey,
- ...(queryKey ?? [{ limit, offset, orderBy }]),
+ ...(queryKey ?? [{ limit, offset, orderBy, variableKeyPattern }]),
];
export type MonitorServiceGetHealthDefaultResponse = Awaited<
ReturnType<typeof MonitorService.getHealth>
diff --git a/airflow/ui/openapi-gen/queries/prefetch.ts
b/airflow/ui/openapi-gen/queries/prefetch.ts
index 566f207f9ae..eab8ea6b356 100644
--- a/airflow/ui/openapi-gen/queries/prefetch.ts
+++ b/airflow/ui/openapi-gen/queries/prefetch.ts
@@ -2183,6 +2183,7 @@ export const prefetchUseVariableServiceGetVariable = (
* @param data.limit
* @param data.offset
* @param data.orderBy
+ * @param data.variableKeyPattern
* @returns VariableCollectionResponse Successful Response
* @throws ApiError
*/
@@ -2192,10 +2193,12 @@ export const prefetchUseVariableServiceGetVariables = (
limit,
offset,
orderBy,
+ variableKeyPattern,
}: {
limit?: number;
offset?: number;
orderBy?: string;
+ variableKeyPattern?: string;
} = {},
) =>
queryClient.prefetchQuery({
@@ -2203,8 +2206,15 @@ export const prefetchUseVariableServiceGetVariables = (
limit,
offset,
orderBy,
+ variableKeyPattern,
}),
- queryFn: () => VariableService.getVariables({ limit, offset, orderBy }),
+ queryFn: () =>
+ VariableService.getVariables({
+ limit,
+ offset,
+ orderBy,
+ variableKeyPattern,
+ }),
});
/**
* Get Health
diff --git a/airflow/ui/openapi-gen/queries/queries.ts
b/airflow/ui/openapi-gen/queries/queries.ts
index 9b9a6dabce9..e90ae58e6b0 100644
--- a/airflow/ui/openapi-gen/queries/queries.ts
+++ b/airflow/ui/openapi-gen/queries/queries.ts
@@ -2580,6 +2580,7 @@ export const useVariableServiceGetVariable = <
* @param data.limit
* @param data.offset
* @param data.orderBy
+ * @param data.variableKeyPattern
* @returns VariableCollectionResponse Successful Response
* @throws ApiError
*/
@@ -2592,21 +2593,28 @@ export const useVariableServiceGetVariables = <
limit,
offset,
orderBy,
+ variableKeyPattern,
}: {
limit?: number;
offset?: number;
orderBy?: string;
+ variableKeyPattern?: string;
} = {},
queryKey?: TQueryKey,
options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
) =>
useQuery<TData, TError>({
queryKey: Common.UseVariableServiceGetVariablesKeyFn(
- { limit, offset, orderBy },
+ { limit, offset, orderBy, variableKeyPattern },
queryKey,
),
queryFn: () =>
- VariableService.getVariables({ limit, offset, orderBy }) as TData,
+ VariableService.getVariables({
+ limit,
+ offset,
+ orderBy,
+ variableKeyPattern,
+ }) as TData,
...options,
});
/**
diff --git a/airflow/ui/openapi-gen/queries/suspense.ts
b/airflow/ui/openapi-gen/queries/suspense.ts
index ba8c34146b4..e7b21338760 100644
--- a/airflow/ui/openapi-gen/queries/suspense.ts
+++ b/airflow/ui/openapi-gen/queries/suspense.ts
@@ -2555,6 +2555,7 @@ export const useVariableServiceGetVariableSuspense = <
* @param data.limit
* @param data.offset
* @param data.orderBy
+ * @param data.variableKeyPattern
* @returns VariableCollectionResponse Successful Response
* @throws ApiError
*/
@@ -2567,21 +2568,28 @@ export const useVariableServiceGetVariablesSuspense = <
limit,
offset,
orderBy,
+ variableKeyPattern,
}: {
limit?: number;
offset?: number;
orderBy?: string;
+ variableKeyPattern?: string;
} = {},
queryKey?: TQueryKey,
options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
) =>
useSuspenseQuery<TData, TError>({
queryKey: Common.UseVariableServiceGetVariablesKeyFn(
- { limit, offset, orderBy },
+ { limit, offset, orderBy, variableKeyPattern },
queryKey,
),
queryFn: () =>
- VariableService.getVariables({ limit, offset, orderBy }) as TData,
+ VariableService.getVariables({
+ limit,
+ offset,
+ orderBy,
+ variableKeyPattern,
+ }) as TData,
...options,
});
/**
diff --git a/airflow/ui/openapi-gen/requests/services.gen.ts
b/airflow/ui/openapi-gen/requests/services.gen.ts
index 9f8c9629bfb..c855ca58092 100644
--- a/airflow/ui/openapi-gen/requests/services.gen.ts
+++ b/airflow/ui/openapi-gen/requests/services.gen.ts
@@ -3010,6 +3010,7 @@ export class VariableService {
* @param data.limit
* @param data.offset
* @param data.orderBy
+ * @param data.variableKeyPattern
* @returns VariableCollectionResponse Successful Response
* @throws ApiError
*/
@@ -3023,6 +3024,7 @@ export class VariableService {
limit: data.limit,
offset: data.offset,
order_by: data.orderBy,
+ variable_key_pattern: data.variableKeyPattern,
},
errors: {
401: "Unauthorized",
diff --git a/airflow/ui/openapi-gen/requests/types.gen.ts
b/airflow/ui/openapi-gen/requests/types.gen.ts
index 76319152010..96a159bad07 100644
--- a/airflow/ui/openapi-gen/requests/types.gen.ts
+++ b/airflow/ui/openapi-gen/requests/types.gen.ts
@@ -2054,6 +2054,7 @@ export type GetVariablesData = {
limit?: number;
offset?: number;
orderBy?: string;
+ variableKeyPattern?: string | null;
};
export type GetVariablesResponse = VariableCollectionResponse;
diff --git a/tests/api_fastapi/core_api/routes/public/test_variables.py
b/tests/api_fastapi/core_api/routes/public/test_variables.py
index 348ca4c1af6..2c7419680dc 100644
--- a/tests/api_fastapi/core_api/routes/public/test_variables.py
+++ b/tests/api_fastapi/core_api/routes/public/test_variables.py
@@ -40,6 +40,11 @@ TEST_VARIABLE_VALUE3 = '{"password": "some_password"}'
TEST_VARIABLE_DESCRIPTION3 = "Some description for the variable"
+TEST_VARIABLE_SEARCH_KEY = "test_variable_search_key"
+TEST_VARIABLE_SEARCH_VALUE = "random search value"
+TEST_VARIABLE_SEARCH_DESCRIPTION = "Some description for the variable"
+
+
@provide_session
def _create_variables(session) -> None:
Variable.set(
@@ -63,6 +68,13 @@ def _create_variables(session) -> None:
session=session,
)
+ Variable.set(
+ key=TEST_VARIABLE_SEARCH_KEY,
+ value=TEST_VARIABLE_SEARCH_VALUE,
+ description=TEST_VARIABLE_SEARCH_DESCRIPTION,
+ session=session,
+ )
+
class TestVariableEndpoint:
@pytest.fixture(autouse=True)
@@ -80,11 +92,11 @@ class TestDeleteVariable(TestVariableEndpoint):
def test_delete_should_respond_204(self, test_client, session):
self.create_variables()
variables = session.query(Variable).all()
- assert len(variables) == 3
+ assert len(variables) == 4
response = test_client.delete(f"/public/variables/{TEST_VARIABLE_KEY}")
assert response.status_code == 204
variables = session.query(Variable).all()
- assert len(variables) == 2
+ assert len(variables) == 3
def test_delete_should_respond_404(self, test_client):
response = test_client.delete(f"/public/variables/{TEST_VARIABLE_KEY}")
@@ -122,6 +134,14 @@ class TestGetVariable(TestVariableEndpoint):
"description": TEST_VARIABLE_DESCRIPTION3,
},
),
+ (
+ TEST_VARIABLE_SEARCH_KEY,
+ {
+ "key": TEST_VARIABLE_SEARCH_KEY,
+ "value": TEST_VARIABLE_SEARCH_VALUE,
+ "description": TEST_VARIABLE_SEARCH_DESCRIPTION,
+ },
+ ),
],
)
def test_get_should_respond_200(self, test_client, session, key,
expected_response):
@@ -143,14 +163,37 @@ class TestGetVariables(TestVariableEndpoint):
"query_params, expected_total_entries, expected_keys",
[
# Filters
- ({}, 3, [TEST_VARIABLE_KEY, TEST_VARIABLE_KEY2,
TEST_VARIABLE_KEY3]),
- ({"limit": 1}, 3, [TEST_VARIABLE_KEY]),
- ({"limit": 1, "offset": 1}, 3, [TEST_VARIABLE_KEY2]),
+ ({}, 4, [TEST_VARIABLE_KEY, TEST_VARIABLE_KEY2,
TEST_VARIABLE_KEY3, TEST_VARIABLE_SEARCH_KEY]),
+ ({"limit": 1}, 4, [TEST_VARIABLE_KEY]),
+ ({"limit": 1, "offset": 1}, 4, [TEST_VARIABLE_KEY2]),
# Sort
- ({"order_by": "id"}, 3, [TEST_VARIABLE_KEY, TEST_VARIABLE_KEY2,
TEST_VARIABLE_KEY3]),
- ({"order_by": "-id"}, 3, [TEST_VARIABLE_KEY3, TEST_VARIABLE_KEY2,
TEST_VARIABLE_KEY]),
- ({"order_by": "key"}, 3, [TEST_VARIABLE_KEY3, TEST_VARIABLE_KEY2,
TEST_VARIABLE_KEY]),
- ({"order_by": "-key"}, 3, [TEST_VARIABLE_KEY, TEST_VARIABLE_KEY2,
TEST_VARIABLE_KEY3]),
+ (
+ {"order_by": "id"},
+ 4,
+ [TEST_VARIABLE_KEY, TEST_VARIABLE_KEY2, TEST_VARIABLE_KEY3,
TEST_VARIABLE_SEARCH_KEY],
+ ),
+ (
+ {"order_by": "-id"},
+ 4,
+ [TEST_VARIABLE_SEARCH_KEY, TEST_VARIABLE_KEY3,
TEST_VARIABLE_KEY2, TEST_VARIABLE_KEY],
+ ),
+ (
+ {"order_by": "key"},
+ 4,
+ [TEST_VARIABLE_KEY3, TEST_VARIABLE_KEY2, TEST_VARIABLE_KEY,
TEST_VARIABLE_SEARCH_KEY],
+ ),
+ (
+ {"order_by": "-key"},
+ 4,
+ [TEST_VARIABLE_SEARCH_KEY, TEST_VARIABLE_KEY,
TEST_VARIABLE_KEY2, TEST_VARIABLE_KEY3],
+ ),
+ # Search
+ (
+ {"variable_key_pattern": "~"},
+ 4,
+ [TEST_VARIABLE_KEY, TEST_VARIABLE_KEY2, TEST_VARIABLE_KEY3,
TEST_VARIABLE_SEARCH_KEY],
+ ),
+ ({"variable_key_pattern": "search"}, 1,
[TEST_VARIABLE_SEARCH_KEY]),
],
)
def test_should_respond_200(