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 9ef89acf85d AIP-38 List providers (#45535)
9ef89acf85d is described below

commit 9ef89acf85dffe69266b716fa0fa1cfa6246b1f3
Author: Linh <[email protected]>
AuthorDate: Fri Jan 31 16:34:13 2025 +0700

    AIP-38 List providers (#45535)
    
    * rebasing
    
    * fix regex
    
    * remove pnpm from package.json
---
 airflow/ui/src/layouts/Nav/AdminButton.tsx |  4 ++
 airflow/ui/src/pages/Providers.tsx         | 83 ++++++++++++++++++++++++++++++
 airflow/ui/src/router.tsx                  |  5 ++
 3 files changed, 92 insertions(+)

diff --git a/airflow/ui/src/layouts/Nav/AdminButton.tsx 
b/airflow/ui/src/layouts/Nav/AdminButton.tsx
index 7889f4ede56..53ad5c9d4d7 100644
--- a/airflow/ui/src/layouts/Nav/AdminButton.tsx
+++ b/airflow/ui/src/layouts/Nav/AdminButton.tsx
@@ -32,6 +32,10 @@ const links = [
     href: "/pools",
     title: "Pools",
   },
+  {
+    href: "/providers",
+    title: "Providers",
+  },
 ];
 
 export const AdminButton = () => (
diff --git a/airflow/ui/src/pages/Providers.tsx 
b/airflow/ui/src/pages/Providers.tsx
new file mode 100644
index 00000000000..8c05f434d5d
--- /dev/null
+++ b/airflow/ui/src/pages/Providers.tsx
@@ -0,0 +1,83 @@
+/*!
+ * 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.
+ */
+import { Box, Heading, Link } from "@chakra-ui/react";
+import type { ColumnDef } from "@tanstack/react-table";
+
+import { useProviderServiceGetProviders } from "openapi/queries";
+import type { ProviderResponse } from "openapi/requests/types.gen";
+import { DataTable } from "src/components/DataTable";
+import { ErrorAlert } from "src/components/ErrorAlert";
+
+const columns: Array<ColumnDef<ProviderResponse>> = [
+  {
+    accessorKey: "package_name",
+    cell: ({ row: { original } }) => (
+      <Link
+        aria-label={original.package_name}
+        color="fg.info"
+        
href={`https://airflow.apache.org/docs/${original.package_name}/${original.version}/`}
+        rel="noopener noreferrer"
+        target="_blank"
+      >
+        {original.package_name}
+      </Link>
+    ),
+    header: "Package Name",
+  },
+  {
+    accessorKey: "version",
+    cell: ({ row: { original } }) => original.version,
+    header: () => "Version",
+  },
+  {
+    accessorKey: "description",
+    cell: ({ row: { original } }) => {
+      const urlRegex = 
/http(s)?:\/\/[\w.-]+(\.?:[\w.-]+)*([#/?][\w!#$%&'()*+,./:;=?@[\]~-]*)?/gu;
+      const urls = original.description.match(urlRegex);
+      const cleanText = original.description.replaceAll(/\n(?:and)?/gu, " 
").split(" ");
+
+      return cleanText.map((part) =>
+        urls?.includes(part) ? (
+          <Link color="fg.info" href={part} key={part} rel="noopener 
noreferrer" target="_blank">
+            {part}
+          </Link>
+        ) : (
+          `${part} `
+        ),
+      );
+    },
+    header: "Description",
+  },
+];
+
+export const Providers = () => {
+  const { data, error } = useProviderServiceGetProviders();
+
+  return (
+    <Box p={2}>
+      <Heading>Providers</Heading>
+      <DataTable
+        columns={columns}
+        data={data?.providers ?? []}
+        errorMessage={<ErrorAlert error={error} />}
+        total={data?.total_entries}
+      />
+    </Box>
+  );
+};
diff --git a/airflow/ui/src/router.tsx b/airflow/ui/src/router.tsx
index 5e5f1b3a576..24a5c7240cb 100644
--- a/airflow/ui/src/router.tsx
+++ b/airflow/ui/src/router.tsx
@@ -31,6 +31,7 @@ import { DagsList } from "src/pages/DagsList";
 import { Dashboard } from "src/pages/Dashboard";
 import { ErrorPage } from "src/pages/Error";
 import { Events } from "src/pages/Events";
+import { Providers } from "src/pages/Providers";
 import { Run } from "src/pages/Run";
 import { Details as DagRunDetails } from "src/pages/Run/Details";
 import { TaskInstances } from "src/pages/Run/TaskInstances";
@@ -70,6 +71,10 @@ export const routerConfig = [
         element: <Pools />,
         path: "pools",
       },
+      {
+        element: <Providers />,
+        path: "providers",
+      },
       {
         children: [
           { element: <Overview />, index: true },

Reply via email to