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):