This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-1-test by this push:
     new 091b53c20b1 [v3-1-test] `issue-59576`: Properly Link Public/Private 
Provider Docs (#59584) (#59728)
091b53c20b1 is described below

commit 091b53c20b154822d1ed1023a41e93b79b2db1f4
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Dec 22 22:37:37 2025 +0100

    [v3-1-test] `issue-59576`: Properly Link Public/Private Provider Docs 
(#59584) (#59728)
    
    * issue-59576: Initial commit
    
    * issue-59576: Generating models
    
    * issue-59576: Linting Providers.tsx
    (cherry picked from commit 1d0c7849ad43ec94480b100c4c1a5e9fa0b4fa15)
    
    Co-authored-by: Jake Roach <[email protected]>
---
 .../airflow/api_fastapi/core_api/datamodels/providers.py  |  1 +
 .../core_api/openapi/v2-rest-api-generated.yaml           |  6 ++++++
 .../api_fastapi/core_api/services/public/providers.py     |  1 +
 airflow-core/src/airflow/providers_manager.py             | 15 +++++++++++++++
 .../src/airflow/ui/openapi-gen/requests/schemas.gen.ts    | 13 ++++++++++++-
 .../src/airflow/ui/openapi-gen/requests/types.gen.ts      |  1 +
 airflow-core/src/airflow/ui/src/pages/Providers.tsx       |  5 ++++-
 airflow-core/tests/unit/always/test_providers_manager.py  |  2 ++
 .../api_fastapi/core_api/routes/public/test_providers.py  |  2 ++
 airflow-ctl/src/airflowctl/api/datamodels/generated.py    |  1 +
 10 files changed, 45 insertions(+), 2 deletions(-)

diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/providers.py 
b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/providers.py
index 8b515fafd2d..ab4381c231b 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/providers.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/providers.py
@@ -26,6 +26,7 @@ class ProviderResponse(BaseModel):
     package_name: str
     description: str
     version: str
+    documentation_url: str | None
 
 
 class ProviderCollectionResponse(BaseModel):
diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
 
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
index 4231c25b16b..45f38707a6e 100644
--- 
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
+++ 
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
@@ -11566,11 +11566,17 @@ components:
         version:
           type: string
           title: Version
+        documentation_url:
+          anyOf:
+          - type: string
+          - type: 'null'
+          title: Documentation Url
       type: object
       required:
       - package_name
       - description
       - version
+      - documentation_url
       title: ProviderResponse
       description: Provider serializer for responses.
     QueuedEventCollectionResponse:
diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/services/public/providers.py 
b/airflow-core/src/airflow/api_fastapi/core_api/services/public/providers.py
index 2f9fa3a1b86..8d659792b97 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/services/public/providers.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/services/public/providers.py
@@ -31,4 +31,5 @@ def _provider_mapper(provider: ProviderInfo) -> 
ProviderResponse:
         package_name=provider.data["package-name"],
         description=_remove_rst_syntax(provider.data["description"]),
         version=provider.version,
+        documentation_url=provider.data["documentation-url"],
     )
diff --git a/airflow-core/src/airflow/providers_manager.py 
b/airflow-core/src/airflow/providers_manager.py
index 20fc79bd7fa..f82a1293449 100644
--- a/airflow-core/src/airflow/providers_manager.py
+++ b/airflow-core/src/airflow/providers_manager.py
@@ -601,6 +601,21 @@ class ProvidersManager(LoggingMixin, metaclass=Singleton):
                     f"The package '{package_name}' from packaging information "
                     f"{provider_info_package_name} do not match. Please make 
sure they are aligned"
                 )
+
+            # issue-59576: Retrieve the project.urls.documentation from 
dist.metadata
+            project_urls = dist.metadata.get_all("Project-URL")
+            documentation_url: str | None = None
+
+            if project_urls:
+                for entry in project_urls:
+                    if "," in entry:
+                        name, url = entry.split(",")
+                        if name.strip().lower() == "documentation":
+                            documentation_url = url
+                            break
+
+            provider_info["documentation-url"] = documentation_url
+
             if package_name not in self._provider_dict:
                 self._provider_dict[package_name] = ProviderInfo(version, 
provider_info)
             else:
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts 
b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
index c2bc9a822d3..ab7aa4942f7 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
@@ -4436,10 +4436,21 @@ export const $ProviderResponse = {
         version: {
             type: 'string',
             title: 'Version'
+        },
+        documentation_url: {
+            anyOf: [
+                {
+                    type: 'string'
+                },
+                {
+                    type: 'null'
+                }
+            ],
+            title: 'Documentation Url'
         }
     },
     type: 'object',
-    required: ['package_name', 'description', 'version'],
+    required: ['package_name', 'description', 'version', 'documentation_url'],
     title: 'ProviderResponse',
     description: 'Provider serializer for responses.'
 } as const;
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts 
b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
index eaf8190a369..33cc7992bad 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
@@ -1183,6 +1183,7 @@ export type ProviderResponse = {
     package_name: string;
     description: string;
     version: string;
+    documentation_url: string | null;
 };
 
 /**
diff --git a/airflow-core/src/airflow/ui/src/pages/Providers.tsx 
b/airflow-core/src/airflow/ui/src/pages/Providers.tsx
index 8602f3534e9..9279fb648d9 100644
--- a/airflow-core/src/airflow/ui/src/pages/Providers.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Providers.tsx
@@ -36,7 +36,10 @@ const createColumns = (translate: TFunction): 
Array<ColumnDef<ProviderResponse>>
       <Link
         aria-label={original.package_name}
         color="fg.info"
-        
href={`https://airflow.apache.org/docs/${original.package_name}/${original.version}/`}
+        href={
+          original.documentation_url ??
+          
`https://airflow.apache.org/docs/${original.package_name}/${original.version}/`
+        }
         rel="noopener noreferrer"
         target="_blank"
       >
diff --git a/airflow-core/tests/unit/always/test_providers_manager.py 
b/airflow-core/tests/unit/always/test_providers_manager.py
index 70f6070bd21..924f4e4c182 100644
--- a/airflow-core/tests/unit/always/test_providers_manager.py
+++ b/airflow-core/tests/unit/always/test_providers_manager.py
@@ -67,8 +67,10 @@ class TestProviderManager:
             for provider in provider_list:
                 package_name = 
provider_manager.providers[provider].data["package-name"]
                 version = provider_manager.providers[provider].version
+                documentation_url = 
provider_manager.providers[provider].data["documentation-url"]
                 assert re.search(r"[0-9]*\.[0-9]*\.[0-9]*.*", version)
                 assert package_name == provider
+                assert isinstance(documentation_url, str)
             # just a coherence check - no exact number as otherwise we would 
have to update
             # several tests if we add new connections/provider which is not 
ideal
             assert len(provider_list) > 65
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_providers.py 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_providers.py
index cb160375578..27cb30bc7ad 100644
--- 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_providers.py
+++ 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_providers.py
@@ -34,6 +34,7 @@ MOCK_PROVIDERS = {
             "name": "Amazon",
             "description": "`Amazon Web Services (AWS) 
<https://aws.amazon.com/>`__.\n",
             "versions": ["1.0.0"],
+            "documentation-url": 
"https://airflow.apache.org/docs/apache-airflow-providers-amazon/1.0.0/";,
         },
     ),
     "apache-airflow-providers-apache-cassandra": ProviderInfo(
@@ -43,6 +44,7 @@ MOCK_PROVIDERS = {
             "name": "Apache Cassandra",
             "description": "`Apache Cassandra 
<http://cassandra.apache.org/>`__.\n",
             "versions": ["1.0.0"],
+            "documentation-url": 
"https://airflow.apache.org/docs/apache-airflow-providers-apache-cassandra/1.0.0/";,
         },
     ),
 }
diff --git a/airflow-ctl/src/airflowctl/api/datamodels/generated.py 
b/airflow-ctl/src/airflowctl/api/datamodels/generated.py
index d6ce7fa10bf..f546515584a 100644
--- a/airflow-ctl/src/airflowctl/api/datamodels/generated.py
+++ b/airflow-ctl/src/airflowctl/api/datamodels/generated.py
@@ -658,6 +658,7 @@ class ProviderResponse(BaseModel):
     package_name: Annotated[str, Field(title="Package Name")]
     description: Annotated[str, Field(title="Description")]
     version: Annotated[str, Field(title="Version")]
+    documentation_url: Annotated[str | None, Field(title="Documentation Url")] 
= None
 
 
 class QueuedEventResponse(BaseModel):

Reply via email to