nrp_cmd.async_client.invenio
22class AsyncInvenioRepositoryClient(AsyncRepositoryClient): 23 """An abstract client for NRP repositories. 24 25 Usually, subclasses of this class are not instantiated directly in your code. 26 To get an instance of a repository, use the high-level method `get_async_client`: 27 28 ``` 29 my_client = await async_client(config?, url=url, refresh=False/True) 30 my_client = await async_client(config?, alias=alias, refresh=False/True) 31 ``` 32 33 and then use the instance. 34 """ 35 36 @classmethod 37 async def can_handle_repository( 38 cls, url: URL | str, verify_tls: bool = True 39 ) -> URL | None: 40 """Return if this client can handle a repository that contains the passed URL. 41 42 This method can make an http request or use any means to check if the repository 43 at the URL can be handled by this client. 44 45 :param url: any url within the repository (root, record api, html, documentation 46 if running on the same host, ...) 47 :param verify_tls: whether to verify tls (should be switched on for production) 48 :return: API url of the server or None if this client can not handle the repository 49 """ 50 connection = AsyncConnection(verify_tls=verify_tls) 51 if isinstance(url, str): 52 url = URL(url) 53 54 # check if the repository is an NRP invenio repository 55 well_known_url = url.with_path("/.well-known/repository") 56 try: 57 data = await connection.get(url=well_known_url, result_class=dict[str, Any]) 58 if "invenio_version" in data: 59 if "api" in data.get("links", {}): 60 return URL(data["links"]["api"]) 61 # fallback for older oarepo-runtime versions 62 return url.with_path("/api") 63 except Exception: 64 pass 65 66 # check if the repository is a plain Invenio RDM repository, such as zenodo 67 try: 68 root_page_data = await connection.get(url=url.with_path("/"), result_class=str) 69 if '<meta name="generator" content="InvenioRDM' in root_page_data: 70 return url.with_path("/api") 71 except Exception: 72 pass 73 return None 74 75 @classmethod 76 async def from_configuration( 77 cls, 78 config: RepositoryConfig, 79 refresh: bool = False, 80 limiter: Limiter | None = None, 81 extra_tokens: dict[URL, str] | None = None, 82 ) -> Self: 83 """Create a client from the given configuration. 84 85 :param config: the configuration for the repository 86 :param refresh: refresh the configuration by calling get_repository_info 87 """ 88 ret = cls(config=config, limiter=limiter, extra_tokens=extra_tokens) 89 if refresh or not config.info: 90 config.info = await ret.get_repository_info(refresh=True) 91 return ret 92 93 def __init__( 94 self, 95 config: RepositoryConfig, 96 limiter: Limiter | None = None, 97 extra_tokens: dict[URL, str] | None = None, 98 ): 99 """Create a new client for the given repository configuration.""" 100 self._config = config 101 tokens: dict[URL, str] = {} 102 if extra_tokens: 103 tokens.update(extra_tokens) 104 if config.token: 105 tokens[config.url] = config.token 106 self._connection = AsyncConnection( 107 limiter=limiter, 108 tokens=tokens, 109 verify_tls=config.verify_tls, 110 retry_count=config.retry_count, 111 retry_after_seconds=config.retry_after_seconds, 112 ) 113 114 async def get_repository_info( 115 self, refresh: bool = True 116 ) -> RepositoryInfo: 117 """Get information about the repository. 118 119 This call is cached inside the RepositoryConfig instance. 120 121 :param include_models: fetch information about the supported models as well 122 :param refresh: refresh the info from the server 123 """ 124 125 if self._config.info and not refresh: 126 return self._config.info 127 128 try: 129 info = await self._connection.get( 130 url=self._config.well_known_repository_url, 131 result_class=RepositoryInfo, 132 ) 133 self._config.info = info 134 135 if not self._config.info.links.models: 136 raise ValueError("The repository does not provide the models link.") 137 138 models = await self._connection.get( 139 url=self._config.info.links.models, 140 result_class=list[ModelInfo], 141 ) 142 143 info.models = { 144 model.type: model for model in models 145 } 146 147 except* (RepositoryClientError, RepositoryCommunicationError) as exc: 148 if is_instance_of_exceptions(exc, StructureError): 149 raise exc 150 # not a NRP based repository, suppose that it is plain invenio rdm 151 info = self._config.info = make_rdm_info(self._config.url) 152 153 return info 154 155 156 @property 157 def records(self) -> AsyncInvenioRecordsClient: 158 """Return client for accessing records.""" 159 assert self._config.info, "Repository info is not available, can not create records client." 160 return AsyncInvenioRecordsClient(self._connection, self._config.info)
An abstract client for NRP repositories.
Usually, subclasses of this class are not instantiated directly in your code.
To get an instance of a repository, use the high-level method get_async_client:
my_client = await async_client(config?, url=url, refresh=False/True)
my_client = await async_client(config?, alias=alias, refresh=False/True)
and then use the instance.
AsyncInvenioRepositoryClient( config: nrp_cmd.config.RepositoryConfig, limiter: nrp_cmd.async_client.connection.Limiter | None = None, extra_tokens: dict[yarl.URL, str] | None = None)
93 def __init__( 94 self, 95 config: RepositoryConfig, 96 limiter: Limiter | None = None, 97 extra_tokens: dict[URL, str] | None = None, 98 ): 99 """Create a new client for the given repository configuration.""" 100 self._config = config 101 tokens: dict[URL, str] = {} 102 if extra_tokens: 103 tokens.update(extra_tokens) 104 if config.token: 105 tokens[config.url] = config.token 106 self._connection = AsyncConnection( 107 limiter=limiter, 108 tokens=tokens, 109 verify_tls=config.verify_tls, 110 retry_count=config.retry_count, 111 retry_after_seconds=config.retry_after_seconds, 112 )
Create a new client for the given repository configuration.
@classmethod
async def
can_handle_repository(cls, url: yarl.URL | str, verify_tls: bool = True) -> yarl.URL | None:
36 @classmethod 37 async def can_handle_repository( 38 cls, url: URL | str, verify_tls: bool = True 39 ) -> URL | None: 40 """Return if this client can handle a repository that contains the passed URL. 41 42 This method can make an http request or use any means to check if the repository 43 at the URL can be handled by this client. 44 45 :param url: any url within the repository (root, record api, html, documentation 46 if running on the same host, ...) 47 :param verify_tls: whether to verify tls (should be switched on for production) 48 :return: API url of the server or None if this client can not handle the repository 49 """ 50 connection = AsyncConnection(verify_tls=verify_tls) 51 if isinstance(url, str): 52 url = URL(url) 53 54 # check if the repository is an NRP invenio repository 55 well_known_url = url.with_path("/.well-known/repository") 56 try: 57 data = await connection.get(url=well_known_url, result_class=dict[str, Any]) 58 if "invenio_version" in data: 59 if "api" in data.get("links", {}): 60 return URL(data["links"]["api"]) 61 # fallback for older oarepo-runtime versions 62 return url.with_path("/api") 63 except Exception: 64 pass 65 66 # check if the repository is a plain Invenio RDM repository, such as zenodo 67 try: 68 root_page_data = await connection.get(url=url.with_path("/"), result_class=str) 69 if '<meta name="generator" content="InvenioRDM' in root_page_data: 70 return url.with_path("/api") 71 except Exception: 72 pass 73 return None
Return if this client can handle a repository that contains the passed URL.
This method can make an http request or use any means to check if the repository at the URL can be handled by this client.
Parameters
- url: any url within the repository (root, record api, html, documentation if running on the same host, ...)
- verify_tls: whether to verify tls (should be switched on for production)
Returns
API url of the server or None if this client can not handle the repository
@classmethod
async def
from_configuration( cls, config: nrp_cmd.config.RepositoryConfig, refresh: bool = False, limiter: nrp_cmd.async_client.connection.Limiter | None = None, extra_tokens: dict[yarl.URL, str] | None = None) -> Self:
75 @classmethod 76 async def from_configuration( 77 cls, 78 config: RepositoryConfig, 79 refresh: bool = False, 80 limiter: Limiter | None = None, 81 extra_tokens: dict[URL, str] | None = None, 82 ) -> Self: 83 """Create a client from the given configuration. 84 85 :param config: the configuration for the repository 86 :param refresh: refresh the configuration by calling get_repository_info 87 """ 88 ret = cls(config=config, limiter=limiter, extra_tokens=extra_tokens) 89 if refresh or not config.info: 90 config.info = await ret.get_repository_info(refresh=True) 91 return ret
Create a client from the given configuration.
Parameters
- config: the configuration for the repository
- refresh: refresh the configuration by calling get_repository_info
114 async def get_repository_info( 115 self, refresh: bool = True 116 ) -> RepositoryInfo: 117 """Get information about the repository. 118 119 This call is cached inside the RepositoryConfig instance. 120 121 :param include_models: fetch information about the supported models as well 122 :param refresh: refresh the info from the server 123 """ 124 125 if self._config.info and not refresh: 126 return self._config.info 127 128 try: 129 info = await self._connection.get( 130 url=self._config.well_known_repository_url, 131 result_class=RepositoryInfo, 132 ) 133 self._config.info = info 134 135 if not self._config.info.links.models: 136 raise ValueError("The repository does not provide the models link.") 137 138 models = await self._connection.get( 139 url=self._config.info.links.models, 140 result_class=list[ModelInfo], 141 ) 142 143 info.models = { 144 model.type: model for model in models 145 } 146 147 except* (RepositoryClientError, RepositoryCommunicationError) as exc: 148 if is_instance_of_exceptions(exc, StructureError): 149 raise exc 150 # not a NRP based repository, suppose that it is plain invenio rdm 151 info = self._config.info = make_rdm_info(self._config.url) 152 153 return info
Get information about the repository.
This call is cached inside the RepositoryConfig instance.
Parameters
- include_models: fetch information about the supported models as well
- refresh: refresh the info from the server
records: nrp_cmd.async_client.invenio.records.AsyncInvenioRecordsClient
156 @property 157 def records(self) -> AsyncInvenioRecordsClient: 158 """Return client for accessing records.""" 159 assert self._config.info, "Repository info is not available, can not create records client." 160 return AsyncInvenioRecordsClient(self._connection, self._config.info)
Return client for accessing records.