This is an automated email from the ASF dual-hosted git repository.
jscheffl 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 5f7844fb19f Export selected variables on variables list page (#45810)
5f7844fb19f is described below
commit 5f7844fb19f3f18cc3026989a2781c78c026d650
Author: Shubham Raj <[email protected]>
AuthorDate: Tue Jan 21 04:24:44 2025 +0530
Export selected variables on variables list page (#45810)
* export
* json download
---
airflow/ui/src/pages/Variables/Variables.tsx | 31 ++++++++++++++++++++++++---
airflow/ui/src/utils/downloadJson.ts | 32 ++++++++++++++++++++++++++++
2 files changed, 60 insertions(+), 3 deletions(-)
diff --git a/airflow/ui/src/pages/Variables/Variables.tsx
b/airflow/ui/src/pages/Variables/Variables.tsx
index 51a0c8c35cc..4b67a33b613 100644
--- a/airflow/ui/src/pages/Variables/Variables.tsx
+++ b/airflow/ui/src/pages/Variables/Variables.tsx
@@ -18,7 +18,7 @@
*/
import { Box, Flex, HStack, Spacer, VStack } from "@chakra-ui/react";
import type { ColumnDef } from "@tanstack/react-table";
-import { useMemo, useState } from "react";
+import { useEffect, useMemo, useState } from "react";
import { FiShare } from "react-icons/fi";
import { useSearchParams } from "react-router-dom";
@@ -34,6 +34,7 @@ import { ActionBar } from "src/components/ui/ActionBar";
import { Checkbox } from "src/components/ui/Checkbox";
import { SearchParamsKeys, type SearchParamsKeysType } from
"src/constants/searchParams";
import { TrimText } from "src/utils/TrimText";
+import { downloadJson } from "src/utils/downloadJson";
import DeleteVariablesButton from "./DeleteVariablesButton";
import ImportVariablesButton from "./ImportVariablesButton";
@@ -112,6 +113,7 @@ export const Variables = () => {
const [variableKeyPattern, setVariableKeyPattern] = useState(
searchParams.get(NAME_PATTERN_PARAM) ?? undefined,
);
+ const [selectedVariables, setSelectedVariables] = useState<Record<string,
string | undefined>>({});
const { pagination, sorting } = tableURLState;
const [sort] = sorting;
const orderBy = sort ? `${sort.desc ? "-" : ""}${sort.id === "value" ?
"_val" : sort.id}` : "-key";
@@ -154,6 +156,29 @@ export const Variables = () => {
setVariableKeyPattern(value);
};
+ useEffect(() => {
+ const newSelection: Record<string, string | undefined> = {
...selectedVariables };
+
+ data?.variables.forEach((variable) => {
+ if (selectedRows.has(variable.key)) {
+ newSelection[variable.key] = variable.value;
+ }
+ });
+
+ // Filter out keys that are not in selectedRows
+ const filteredSelection = Object.keys(newSelection)
+ .filter((key) => selectedRows.has(key))
+ .reduce<Record<string, string | undefined>>((acc, key) => {
+ acc[key] = newSelection[key];
+
+ return acc;
+ }, {});
+
+ if (Object.keys(filteredSelection).length !==
Object.keys(selectedVariables).length) {
+ setSelectedVariables(filteredSelection);
+ }
+ }, [selectedRows, data, selectedVariables]);
+
return (
<>
<VStack alignItems="none">
@@ -190,8 +215,8 @@ export const Variables = () => {
<Tooltip content="Delete selected variables">
<DeleteVariablesButton clearSelections={clearSelections}
deleteKeys={[...selectedRows.keys()]} />
</Tooltip>
- <Tooltip content="Export selected variable coming soon..">
- <Button disabled size="sm" variant="outline">
+ <Tooltip content="Export selected variables">
+ <Button onClick={() => downloadJson(selectedVariables,
"variables")} size="sm" variant="outline">
<FiShare />
Export
</Button>
diff --git a/airflow/ui/src/utils/downloadJson.ts
b/airflow/ui/src/utils/downloadJson.ts
new file mode 100644
index 00000000000..d9a3f81c7ad
--- /dev/null
+++ b/airflow/ui/src/utils/downloadJson.ts
@@ -0,0 +1,32 @@
+/*!
+ * 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.
+ */
+
+export const downloadJson = (data: Record<string, string | undefined>, name:
string) => {
+ const jsonData = JSON.stringify(data, undefined, 2);
+ const blob = new Blob([jsonData], { type: "application/json" });
+ const url = URL.createObjectURL(blob);
+
+ const link = document.createElement("a");
+
+ link.href = url;
+ link.download = `${name}.json`;
+ link.click();
+
+ URL.revokeObjectURL(url);
+};