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 000d2da0c8 AIP-84 Delete Variable (#42798)
000d2da0c8 is described below

commit 000d2da0c86e68731f7a544f0c394c52121403f9
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Tue Oct 8 15:16:13 2024 +0800

    AIP-84 Delete Variable (#42798)
---
 .../api_connexion/endpoints/variable_endpoint.py   |  2 +
 airflow/api_fastapi/openapi/v1-generated.yaml      | 41 ++++++++++++++
 airflow/api_fastapi/views/public/__init__.py       |  2 +
 .../views/public/{__init__.py => variables.py}     | 26 +++++++--
 airflow/ui/openapi-gen/queries/common.ts           |  4 ++
 airflow/ui/openapi-gen/queries/queries.ts          | 40 +++++++++++++
 airflow/ui/openapi-gen/requests/services.gen.ts    | 30 ++++++++++
 airflow/ui/openapi-gen/requests/types.gen.ts       | 33 +++++++++++
 tests/api_fastapi/views/public/test_variables.py   | 66 ++++++++++++++++++++++
 9 files changed, 238 insertions(+), 6 deletions(-)

diff --git a/airflow/api_connexion/endpoints/variable_endpoint.py 
b/airflow/api_connexion/endpoints/variable_endpoint.py
index 1375484a42..9413f91586 100644
--- a/airflow/api_connexion/endpoints/variable_endpoint.py
+++ b/airflow/api_connexion/endpoints/variable_endpoint.py
@@ -31,6 +31,7 @@ from airflow.api_connexion.parameters import apply_sorting, 
check_limit, format_
 from airflow.api_connexion.schemas.variable_schema import 
variable_collection_schema, variable_schema
 from airflow.models import Variable
 from airflow.security import permissions
+from airflow.utils.api_migration import mark_fastapi_migration_done
 from airflow.utils.log.action_logger import action_event_from_permission
 from airflow.utils.session import NEW_SESSION, provide_session
 from airflow.www.decorators import action_logging
@@ -43,6 +44,7 @@ if TYPE_CHECKING:
 RESOURCE_EVENT_PREFIX = "variable"
 
 
+@mark_fastapi_migration_done
 @security.requires_access_variable("DELETE")
 @action_logging(
     event=action_event_from_permission(
diff --git a/airflow/api_fastapi/openapi/v1-generated.yaml 
b/airflow/api_fastapi/openapi/v1-generated.yaml
index 272d0b6703..f3bc8612bf 100644
--- a/airflow/api_fastapi/openapi/v1-generated.yaml
+++ b/airflow/api_fastapi/openapi/v1-generated.yaml
@@ -455,6 +455,47 @@ paths:
             application/json:
               schema:
                 $ref: '#/components/schemas/HTTPValidationError'
+  /public/variables/{variable_key}:
+    delete:
+      tags:
+      - Variable
+      summary: Delete Variable
+      description: Delete a variable entry.
+      operationId: delete_variable
+      parameters:
+      - name: variable_key
+        in: path
+        required: true
+        schema:
+          type: string
+          title: Variable Key
+      responses:
+        '204':
+          description: Successful Response
+        '401':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/HTTPExceptionResponse'
+          description: Unauthorized
+        '403':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/HTTPExceptionResponse'
+          description: Forbidden
+        '404':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/HTTPExceptionResponse'
+          description: Not Found
+        '422':
+          description: Validation Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/HTTPValidationError'
 components:
   schemas:
     ConnectionResponse:
diff --git a/airflow/api_fastapi/views/public/__init__.py 
b/airflow/api_fastapi/views/public/__init__.py
index 9c0eefebb8..4e02d9ab43 100644
--- a/airflow/api_fastapi/views/public/__init__.py
+++ b/airflow/api_fastapi/views/public/__init__.py
@@ -19,6 +19,7 @@ from __future__ import annotations
 
 from airflow.api_fastapi.views.public.connections import connections_router
 from airflow.api_fastapi.views.public.dags import dags_router
+from airflow.api_fastapi.views.public.variables import variables_router
 from airflow.api_fastapi.views.router import AirflowRouter
 
 public_router = AirflowRouter(prefix="/public")
@@ -26,3 +27,4 @@ public_router = AirflowRouter(prefix="/public")
 
 public_router.include_router(dags_router)
 public_router.include_router(connections_router)
+public_router.include_router(variables_router)
diff --git a/airflow/api_fastapi/views/public/__init__.py 
b/airflow/api_fastapi/views/public/variables.py
similarity index 52%
copy from airflow/api_fastapi/views/public/__init__.py
copy to airflow/api_fastapi/views/public/variables.py
index 9c0eefebb8..e4edb8601f 100644
--- a/airflow/api_fastapi/views/public/__init__.py
+++ b/airflow/api_fastapi/views/public/variables.py
@@ -14,15 +14,29 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-
 from __future__ import annotations
 
-from airflow.api_fastapi.views.public.connections import connections_router
-from airflow.api_fastapi.views.public.dags import dags_router
+from fastapi import Depends, HTTPException
+from sqlalchemy.orm import Session
+from typing_extensions import Annotated
+
+from airflow.api_fastapi.db.common import get_session
+from airflow.api_fastapi.openapi.exceptions import 
create_openapi_http_exception_doc
 from airflow.api_fastapi.views.router import AirflowRouter
+from airflow.models.variable import Variable
 
-public_router = AirflowRouter(prefix="/public")
+variables_router = AirflowRouter(tags=["Variable"], prefix="/variables")
 
 
-public_router.include_router(dags_router)
-public_router.include_router(connections_router)
+@variables_router.delete(
+    "/{variable_key}",
+    status_code=204,
+    responses=create_openapi_http_exception_doc([401, 403, 404]),
+)
+async def delete_variable(
+    variable_key: str,
+    session: Annotated[Session, Depends(get_session)],
+):
+    """Delete a variable entry."""
+    if Variable.delete(variable_key, session) == 0:
+        raise HTTPException(404, f"The Variable with key: `{variable_key}` was 
not found")
diff --git a/airflow/ui/openapi-gen/queries/common.ts 
b/airflow/ui/openapi-gen/queries/common.ts
index ff7bb3995c..afd7afd5bc 100644
--- a/airflow/ui/openapi-gen/queries/common.ts
+++ b/airflow/ui/openapi-gen/queries/common.ts
@@ -5,6 +5,7 @@ import {
   AssetService,
   ConnectionService,
   DagService,
+  VariableService,
 } from "../requests/services.gen";
 import { DagRunState } from "../requests/types.gen";
 
@@ -119,3 +120,6 @@ export type DagServicePatchDagMutationResult = Awaited<
 export type ConnectionServiceDeleteConnectionMutationResult = Awaited<
   ReturnType<typeof ConnectionService.deleteConnection>
 >;
+export type VariableServiceDeleteVariableMutationResult = Awaited<
+  ReturnType<typeof VariableService.deleteVariable>
+>;
diff --git a/airflow/ui/openapi-gen/queries/queries.ts 
b/airflow/ui/openapi-gen/queries/queries.ts
index 4aa627d74f..22d58eadda 100644
--- a/airflow/ui/openapi-gen/queries/queries.ts
+++ b/airflow/ui/openapi-gen/queries/queries.ts
@@ -10,6 +10,7 @@ import {
   AssetService,
   ConnectionService,
   DagService,
+  VariableService,
 } from "../requests/services.gen";
 import { DAGPatchBody, DagRunState } from "../requests/types.gen";
 import * as Common from "./common";
@@ -345,3 +346,42 @@ export const useConnectionServiceDeleteConnection = <
       }) as unknown as Promise<TData>,
     ...options,
   });
+/**
+ * Delete Variable
+ * Delete a variable entry.
+ * @param data The data for the request.
+ * @param data.variableKey
+ * @returns void Successful Response
+ * @throws ApiError
+ */
+export const useVariableServiceDeleteVariable = <
+  TData = Common.VariableServiceDeleteVariableMutationResult,
+  TError = unknown,
+  TContext = unknown,
+>(
+  options?: Omit<
+    UseMutationOptions<
+      TData,
+      TError,
+      {
+        variableKey: string;
+      },
+      TContext
+    >,
+    "mutationFn"
+  >,
+) =>
+  useMutation<
+    TData,
+    TError,
+    {
+      variableKey: string;
+    },
+    TContext
+  >({
+    mutationFn: ({ variableKey }) =>
+      VariableService.deleteVariable({
+        variableKey,
+      }) as unknown as Promise<TData>,
+    ...options,
+  });
diff --git a/airflow/ui/openapi-gen/requests/services.gen.ts 
b/airflow/ui/openapi-gen/requests/services.gen.ts
index 9921aebb79..72fd2f68f1 100644
--- a/airflow/ui/openapi-gen/requests/services.gen.ts
+++ b/airflow/ui/openapi-gen/requests/services.gen.ts
@@ -17,6 +17,8 @@ import type {
   DeleteConnectionResponse,
   GetConnectionData,
   GetConnectionResponse,
+  DeleteVariableData,
+  DeleteVariableResponse,
 } from "./types.gen";
 
 export class AssetService {
@@ -246,3 +248,31 @@ export class ConnectionService {
     });
   }
 }
+
+export class VariableService {
+  /**
+   * Delete Variable
+   * Delete a variable entry.
+   * @param data The data for the request.
+   * @param data.variableKey
+   * @returns void Successful Response
+   * @throws ApiError
+   */
+  public static deleteVariable(
+    data: DeleteVariableData,
+  ): CancelablePromise<DeleteVariableResponse> {
+    return __request(OpenAPI, {
+      method: "DELETE",
+      url: "/public/variables/{variable_key}",
+      path: {
+        variable_key: data.variableKey,
+      },
+      errors: {
+        401: "Unauthorized",
+        403: "Forbidden",
+        404: "Not Found",
+        422: "Validation Error",
+      },
+    });
+  }
+}
diff --git a/airflow/ui/openapi-gen/requests/types.gen.ts 
b/airflow/ui/openapi-gen/requests/types.gen.ts
index cdb2379d01..d07d980397 100644
--- a/airflow/ui/openapi-gen/requests/types.gen.ts
+++ b/airflow/ui/openapi-gen/requests/types.gen.ts
@@ -222,6 +222,12 @@ export type GetConnectionData = {
 
 export type GetConnectionResponse = ConnectionResponse;
 
+export type DeleteVariableData = {
+  variableKey: string;
+};
+
+export type DeleteVariableResponse = void;
+
 export type $OpenApiTs = {
   "/ui/next_run_assets/{dag_id}": {
     get: {
@@ -398,4 +404,31 @@ export type $OpenApiTs = {
       };
     };
   };
+  "/public/variables/{variable_key}": {
+    delete: {
+      req: DeleteVariableData;
+      res: {
+        /**
+         * Successful Response
+         */
+        204: void;
+        /**
+         * Unauthorized
+         */
+        401: HTTPExceptionResponse;
+        /**
+         * Forbidden
+         */
+        403: HTTPExceptionResponse;
+        /**
+         * Not Found
+         */
+        404: HTTPExceptionResponse;
+        /**
+         * Validation Error
+         */
+        422: HTTPValidationError;
+      };
+    };
+  };
 };
diff --git a/tests/api_fastapi/views/public/test_variables.py 
b/tests/api_fastapi/views/public/test_variables.py
new file mode 100644
index 0000000000..56d65b98b6
--- /dev/null
+++ b/tests/api_fastapi/views/public/test_variables.py
@@ -0,0 +1,66 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import pytest
+
+from airflow.models.variable import Variable
+from airflow.utils.session import provide_session
+from tests.test_utils.db import clear_db_variables
+
+pytestmark = pytest.mark.db_test
+
+TEST_VARIABLE_KEY = "test_variable_key"
+TEST_VARIABLE_VAL = 3
+TEST_VARIABLE_DESCRIPTION = "Some description for the variable"
+TEST_CONN_TYPE = "test_type"
+
+
+@provide_session
+def _create_variable(session) -> None:
+    Variable.set(
+        key=TEST_VARIABLE_KEY, value=TEST_VARIABLE_VAL, 
description=TEST_VARIABLE_DESCRIPTION, session=session
+    )
+
+
+class TestVariableEndpoint:
+    @pytest.fixture(autouse=True)
+    def setup(self) -> None:
+        clear_db_variables()
+
+    def teardown_method(self) -> None:
+        clear_db_variables()
+
+    def create_variable(self):
+        _create_variable()
+
+
+class TestDeleteVariable(TestVariableEndpoint):
+    def test_delete_should_respond_204(self, test_client, session):
+        self.create_variable()
+        variables = session.query(Variable).all()
+        assert len(variables) == 1
+        response = test_client.delete(f"/public/variables/{TEST_VARIABLE_KEY}")
+        assert response.status_code == 204
+        variables = session.query(Variable).all()
+        assert len(variables) == 0
+
+    def test_delete_should_respond_404(self, test_client):
+        response = test_client.delete(f"/public/variables/{TEST_VARIABLE_KEY}")
+        assert response.status_code == 404
+        body = response.json()
+        assert f"The Variable with key: `{TEST_VARIABLE_KEY}` was not found" 
== body["detail"]

Reply via email to