diff --git a/openeo/metadata.py b/openeo/metadata.py index 9bb8581c9..b8a884398 100644 --- a/openeo/metadata.py +++ b/openeo/metadata.py @@ -505,8 +505,9 @@ class CollectionMetadata(CubeMetadata): """ - def __init__(self, metadata: dict, dimensions: List[Dimension] = None): + def __init__(self, metadata: dict, dimensions: List[Dimension] = None, connection = None): self._orig_metadata = metadata + self._connection = connection if dimensions is None: dimensions = self._parse_dimensions(self._orig_metadata) @@ -626,7 +627,8 @@ def extent(self) -> dict: return self._orig_metadata.get("extent") def _repr_html_(self): - return render_component("collection", data=self._orig_metadata) + federation = self._connection.capabilities().ext_federation_backend_details() + return render_component("collection", data=self._orig_metadata, parameters={'federation': federation}) def __str__(self) -> str: bands = self.band_names if self.has_band_dimension() else "no bands dimension" diff --git a/openeo/rest/connection.py b/openeo/rest/connection.py index 398f5fffd..91f5fce8a 100644 --- a/openeo/rest/connection.py +++ b/openeo/rest/connection.py @@ -713,7 +713,7 @@ def list_collections(self) -> CollectionListingResponse: # TODO: add caching #383, but reset cache on auth change #254 # TODO #677 add pagination support? data = self.get("/collections", expected_status=200).json() - return CollectionListingResponse(response_data=data) + return CollectionListingResponse(response_data=data, connection=self) def list_collection_ids(self) -> List[str]: """ @@ -755,7 +755,11 @@ def list_file_formats(self) -> dict: key="file_formats", load=lambda: self.get('/file_formats', expected_status=200).json() ) - return VisualDict("file-formats", data=formats) + federation = self.capabilities().ext_federation_backend_details() + return VisualDict("file-formats", data=formats, parameters={ + 'missing': formats.get("federation:missing", None), + 'federation': federation + }) def list_service_types(self) -> dict: """ @@ -779,7 +783,8 @@ def list_udf_runtimes(self) -> dict: key="udf_runtimes", load=lambda: self.get('/udf_runtimes', expected_status=200).json() ) - return VisualDict("udf-runtimes", data=runtimes) + federation = self.capabilities().ext_federation_backend_details() + return VisualDict("udf-runtimes", data=runtimes, parameters={'federation': federation}) def list_services(self) -> dict: """ @@ -789,7 +794,12 @@ def list_services(self) -> dict: """ # TODO return parsed service objects services = self.get('/services', expected_status=200).json()["services"] - return VisualList("data-table", data=services, parameters={'columns': 'services'}) + federation = self.capabilities().ext_federation_backend_details() + return VisualList("data-table", data=services, parameters={ + 'columns': 'services', + 'missing': services.get("federation:missing", None), + 'federation': federation + }) def describe_collection(self, collection_id: str) -> dict: """ @@ -806,7 +816,8 @@ def describe_collection(self, collection_id: str) -> dict: # TODO: duplication with `Connection.collection_metadata`: deprecate one or the other? # TODO: add caching #383 data = self.get(f"/collections/{collection_id}", expected_status=200).json() - return VisualDict("collection", data=data) + federation = self.capabilities().ext_federation_backend_details() + return VisualDict("collection", data=data, parameters={'federation': federation}) def collection_items( self, @@ -844,11 +855,12 @@ def collection_items( if limit is not None and limit > 0: params['limit'] = limit - return paginate(self, url, params, lambda response, page: VisualDict("items", data = response, parameters = {'show-map': True, 'heading': 'Page {} - Items'.format(page)})) + federation = self.capabilities().ext_federation_backend_details() + return paginate(self, url, params, lambda response, page: VisualDict("items", data = response, parameters = {'show-map': True, 'heading': 'Page {} - Items'.format(page), 'federation': federation})) def collection_metadata(self, name) -> CollectionMetadata: # TODO: duplication with `Connection.describe_collection`: deprecate one or the other? - return CollectionMetadata(metadata=self.describe_collection(name)) + return CollectionMetadata(metadata=self.describe_collection(name), connection=self) def list_processes(self, namespace: Optional[str] = None) -> ProcessListingResponse: """ @@ -870,7 +882,7 @@ def list_processes(self, namespace: Optional[str] = None) -> ProcessListingRespo ) else: response = self.get("/processes/" + namespace, expected_status=200).json() - return ProcessListingResponse(response_data=response) + return ProcessListingResponse(response_data=response, connection=self) def describe_process(self, id: str, namespace: Optional[str] = None) -> dict: """ @@ -883,9 +895,14 @@ def describe_process(self, id: str, namespace: Optional[str] = None) -> dict: """ processes = self.list_processes(namespace) + federation = self.capabilities().ext_federation_backend_details() for process in processes: if process["id"] == id: - return VisualDict("process", data=process, parameters={'show-graph': True, 'provide-download': False}) + return VisualDict("process", data=process, parameters={ + 'show-graph': True, + 'provide-download': False, + 'federation': federation + }) raise OpenEoClientException("Process does not exist.") @@ -912,7 +929,7 @@ def list_jobs(self, limit: Union[int, None] = 100) -> JobListingResponse: # TODO: Parse the result so that Job classes returned? # TODO: when pagination is enabled: how to expose link to next page? resp = self.get("/jobs", params={"limit": limit}, expected_status=200).json() - return JobListingResponse(response_data=resp) + return JobListingResponse(response_data=resp, connection=self) def assert_user_defined_process_support(self): """ @@ -975,7 +992,7 @@ def list_user_defined_processes(self) -> ProcessListingResponse: # TODO #677 add pagination support? self.assert_user_defined_process_support() data = self.get("/process_graphs", expected_status=200).json() - return ProcessListingResponse(response_data=data) + return ProcessListingResponse(response_data=data, connection=self) def user_defined_process(self, user_defined_process_id: str) -> RESTUserDefinedProcess: """ @@ -1475,9 +1492,14 @@ def list_files(self) -> List[UserFile]: :return: List of the user-uploaded files. """ - files = self.get('/files', expected_status=200).json()['files'] - files = [UserFile.from_metadata(metadata=f, connection=self) for f in files] - return VisualList("data-table", data=files, parameters={'columns': 'files'}) + data = self.get('/files', expected_status=200).json() + files = [UserFile.from_metadata(metadata=f, connection=self) for f in data.get('files', [])] + federation = self.capabilities().ext_federation_backend_details() + return VisualList("data-table", data=files, parameters={ + 'columns': 'files', + 'missing': data.get('federation:missing', None), + 'federation': federation, + }) def get_file( self, path: Union[str, PurePosixPath], metadata: Optional[dict] = None diff --git a/openeo/rest/job.py b/openeo/rest/job.py index a76c7bac7..c38e8f894 100644 --- a/openeo/rest/job.py +++ b/openeo/rest/job.py @@ -63,8 +63,11 @@ def __repr__(self): def _repr_html_(self): data = self.describe() - currency = self.connection.capabilities().currency() - return render_component('job', data=data, parameters={'currency': currency}) + capabilities = self.connection.capabilities() + return render_component('job', data=data, parameters={ + 'currency': capabilities.currency(), + 'federation': capabilities.ext_federation_backend_details(), + }) @openeo_endpoint("GET /jobs/{job_id}") def describe(self) -> dict: @@ -217,7 +220,7 @@ def logs(self, offset: Optional[str] = None, level: Union[str, int, None] = None if level is not None: params["level"] = log_level_name(level) response_data = self.connection.get(url, params=params, expected_status=200).json() - return LogsResponse(response_data=response_data, log_level=level) + return LogsResponse(response_data=response_data, log_level=level, connection=self.connection) @deprecated("Use start_and_wait instead", version="0.39.0") def run_synchronous( diff --git a/openeo/rest/models/general.py b/openeo/rest/models/general.py index c59a9c470..3b3174473 100644 --- a/openeo/rest/models/general.py +++ b/openeo/rest/models/general.py @@ -43,23 +43,33 @@ class CollectionListingResponse(list): but now also provides methods/properties to access additional response data. :param response_data: response data from a ``GET /collections`` request + :param connection: optional connection object to use for federation extension .. seealso:: :py:meth:`openeo.rest.connection.Connection.list_collections()` .. versionadded:: 0.38.0 """ - __slots__ = ["_data"] + __slots__ = ["_data", "_connection"] - def __init__(self, response_data: dict): + def __init__(self, response_data: dict, connection = None): self._data = response_data + self._connection = connection # Mimic original list of collection metadata dictionaries super().__init__(response_data["collections"]) self.ext_federation_missing(auto_warn=True) def _repr_html_(self): - return render_component(component="collections", data=self) + federation = self._connection.capabilities().ext_federation_backend_details() if self._connection else None + return render_component( + component="collections", + data=self, + parameters={ + "missing": self.ext_federation_missing(), + "federation": federation, + } + ) @property def links(self) -> List[Link]: @@ -94,24 +104,34 @@ class ProcessListingResponse(list): but now also provides methods/properties to access additional response data. :param response_data: response data from a ``GET /processes`` request + :param connection: optional connection object to use for federation extension .. seealso:: :py:meth:`openeo.rest.connection.Connection.list_processes()` .. versionadded:: 0.38.0 """ - __slots__ = ["_data"] + __slots__ = ["_data", "_connection"] - def __init__(self, response_data: dict): + def __init__(self, response_data: dict, connection = None): self._data = response_data + self._connection = connection # Mimic original list of process metadata dictionaries super().__init__(response_data["processes"]) self.ext_federation_missing(auto_warn=True) def _repr_html_(self): + federation = self._connection.capabilities().ext_federation_backend_details() if self._connection else None return render_component( - component="processes", data=self, parameters={"show-graph": True, "provide-download": False} + component="processes", + data=self, + parameters={ + "show-graph": True, + "provide-download": False, + "missing": self.ext_federation_missing(), + "federation": federation, + } ) @property @@ -148,23 +168,34 @@ class JobListingResponse(list): but now also provides methods/properties to access additional response data. :param response_data: response data from a ``GET /jobs`` request + :param connection: optional connection object to use for federation extension .. seealso:: :py:meth:`openeo.rest.connection.Connection.list_jobs()` .. versionadded:: 0.38.0 """ - __slots__ = ["_data"] + __slots__ = ["_data", "_connection"] - def __init__(self, response_data: dict): + def __init__(self, response_data: dict, connection = None): self._data = response_data + self._connection = connection # Mimic original list of process metadata dictionaries super().__init__(response_data["jobs"]) self.ext_federation_missing(auto_warn=True) def _repr_html_(self): - return render_component(component="data-table", data=self, parameters={"columns": "jobs"}) + federation = self._connection.capabilities().ext_federation_backend_details() if self._connection else None + return render_component( + component="data-table", + data=self, + parameters={ + "columns": "jobs", + "missing": self.ext_federation_missing(), + "federation": federation, + } + ) @property def links(self) -> List[Link]: @@ -202,6 +233,7 @@ class LogsResponse(list): :param response_data: response data from a ``GET /jobs/{job_id}/logs`` or ``GET /services/{service_id}/logs`` request. + :param connection: optional connection object to use for federation extension .. seealso:: :py:meth:`~openeo.rest.job.BatchJob.logs()` and :py:meth:`~openeo.rest.service.Service.logs()` @@ -209,10 +241,11 @@ class LogsResponse(list): .. versionadded:: 0.38.0 """ - __slots__ = ["_data"] + __slots__ = ["_data", "_connection"] - def __init__(self, response_data: dict, *, log_level: Optional[str] = None): + def __init__(self, response_data: dict, *, log_level: Optional[str] = None, connection = None): self._data = response_data + self._connection = connection logs = response_data.get("logs", []) # Extra client-side level filtering (in case the back-end does not support that) @@ -237,7 +270,15 @@ def accept_level(level: str) -> bool: self.ext_federation_missing(auto_warn=True) def _repr_html_(self): - return render_component(component="logs", data=self) + federation = self._connection.capabilities().ext_federation_backend_details() if self._connection else None + return render_component( + component="logs", + data=self, + parameters={ + "missing": self.ext_federation_missing(), + "federation": federation, + } + ) @property def logs(self) -> List[LogEntry]: diff --git a/openeo/rest/service.py b/openeo/rest/service.py index c49a89ec9..e29257ed8 100644 --- a/openeo/rest/service.py +++ b/openeo/rest/service.py @@ -25,8 +25,11 @@ def __repr__(self): def _repr_html_(self): data = self.describe_service() - currency = self.connection.capabilities().currency() - return VisualDict('service', data = data, parameters = {'currency': currency}) + capabilities = self.connection.capabilities() + return VisualDict('service', data = data, parameters = { + 'currency': capabilities.currency(), + 'federation': capabilities.ext_federation_backend_details(), + }) def describe_service(self): """ Get all information about a secondary web service.""" @@ -52,4 +55,4 @@ def logs(self, offset: Optional[str] = None, level: Union[str, int, None] = None if level is not None: params["level"] = log_level_name(level) response_data = self.connection.get(url, params=params, expected_status=200).json() - return LogsResponse(response_data=response_data, log_level=level) + return LogsResponse(response_data=response_data, log_level=level, connection=self.connection) diff --git a/openeo/rest/udp.py b/openeo/rest/udp.py index f9b61c9b8..322441780 100644 --- a/openeo/rest/udp.py +++ b/openeo/rest/udp.py @@ -89,7 +89,12 @@ def __init__(self, user_defined_process_id: str, connection: Connection): def _repr_html_(self): process = self.describe() - return render_component('process', data=process, parameters = {'show-graph': True, 'provide-download': False}) + federation = self._connection.capabilities().ext_federation_backend_details() + return render_component('process', data=process, parameters = { + 'show-graph': True, + 'provide-download': False, + 'federation': federation, + }) def store( self,