2222import logging
2323import os
2424from collections import OrderedDict
25- from typing import Dict , Tuple
25+ from typing import Dict , Set , Tuple
2626
2727import jsonschema
2828import yaml
@@ -68,6 +68,7 @@ def __init__(self):
6868 # Keeps dict of hooks keyed by connection type and value is
6969 # Tuple: connection class, connection_id_attribute_name
7070 self ._hooks_dict : Dict [str , Tuple [str , str ]] = {}
71+ self ._extra_link_class_name_set : Set [str ] = set ()
7172 self ._validator = _create_validator ()
7273 # Local source folders are loaded first. They should take precedence over the package ones for
7374 # Development purpose. In production provider.yaml files are not present in the 'airflow" directory
@@ -78,6 +79,7 @@ def __init__(self):
7879 self ._discover_hooks ()
7980 self ._provider_dict = OrderedDict (sorted (self .providers .items ()))
8081 self ._hooks_dict = OrderedDict (sorted (self .hooks .items ()))
82+ self ._discover_extra_links ()
8183
8284 def _discover_all_providers_from_packages (self ) -> None :
8385 """
@@ -224,6 +226,32 @@ def _add_hook(self, hook_class_name, provider_package) -> None:
224226
225227 self ._hooks_dict [conn_type ] = (hook_class_name , connection_id_attribute_name )
226228
229+ def _discover_extra_links (self ) -> None :
230+ """Retrieves all extra links defined in the providers"""
231+ for provider_package , (_ , provider ) in self ._provider_dict .items ():
232+ if provider .get ("extra-links" ):
233+ for extra_link in provider ["extra-links" ]:
234+ self ._add_extra_link (extra_link , provider_package )
235+
236+ def _add_extra_link (self , extra_link_class_name , provider_package ) -> None :
237+ """
238+ Adds extra link class name to the list of classes
239+ :param extra_link_class_name: name of the class to add
240+ :param provider_package: provider package adding the link
241+ :return:
242+ """
243+ if provider_package .startswith ("apache-airflow" ):
244+ provider_path = provider_package [len ("apache-" ) :].replace ("-" , "." )
245+ if not extra_link_class_name .startswith (provider_path ):
246+ log .warning (
247+ "Sanity check failed when importing '%s' from '%s' package. It should start with '%s'" ,
248+ extra_link_class_name ,
249+ provider_package ,
250+ provider_path ,
251+ )
252+ return
253+ self ._extra_link_class_name_set .add (extra_link_class_name )
254+
227255 @property
228256 def providers (self ):
229257 """Returns information about available providers."""
@@ -233,3 +261,8 @@ def providers(self):
233261 def hooks (self ):
234262 """Returns dictionary of connection_type-to-hook mapping"""
235263 return self ._hooks_dict
264+
265+ @property
266+ def extra_links_class_names (self ):
267+ """Returns set of extra link class names."""
268+ return sorted (list (self ._extra_link_class_name_set ))
0 commit comments