1"""Experimental implementation of Asset Administration Shell Registry related API calls."""
5from pathlib
import Path
6from typing
import TYPE_CHECKING, Any
11 from aas_http_client.classes.client.aas_client
import AasHttpClient
14from pydantic
import BaseModel
25_logger = logging.getLogger(__name__)
29 """Implementation of Asset Administration Shell Registry related API calls."""
31 def __init__(self, client:
"AasHttpClient"):
32 """Initializes the ExperimentalImplementation with the given client."""
35 session = client.get_session()
38 "HTTP session is not initialized in the client. Call 'initialize()' method of the client before creating ExperimentalImplementation instance."
41 self._session: requests.Session = session
45 """Retrieves concept descriptions. Experimental feature - may not be supported by all servers."""
46 url = f
"{self._client.base_url}/concept-descriptions"
51 response = self._session.get(url, timeout=self.
_client.time_out)
52 _logger.debug(f
"Call REST API url '{response.url}'")
54 if response.status_code == STATUS_CODE_404:
55 if self.
_client.get_log_intensity() == LogIntensity.HIGH:
56 _logger.warning(
"Concept descriptions not found.")
57 elif self.
_client.get_log_intensity() == LogIntensity.STANDARD:
58 _logger.debug(
"Concept descriptions not found.")
60 _logger.debug(response.text)
63 if response.status_code != STATUS_CODE_200:
64 log_response(response)
67 except requests.exceptions.RequestException
as e:
68 _logger.error(f
"Error calling REST API: {e}")
71 return response.json()
75 """Retrieves a specific concept description by its identifier. Experimental feature - may not be supported by all servers.
77 :param concept_description_identifier: The unique identifier of the concept description
78 :return: Concept description data as a dictionary or None if not found
80 url = f
"{self._client.base_url}/concept-descriptions/{concept_description_identifier}"
85 response = self._session.get(url, timeout=self.
_client.time_out)
86 _logger.debug(f
"Call REST API url '{response.url}'")
88 if response.status_code == STATUS_CODE_404:
89 if self.
_client.get_log_intensity() == LogIntensity.HIGH:
90 _logger.warning(f
"Concept description with id '{concept_description_identifier}' not found.")
91 elif self.
_client.get_log_intensity() == LogIntensity.STANDARD:
92 _logger.debug(f
"Concept description with id '{concept_description_identifier}' not found.")
94 _logger.debug(response.text)
97 if response.status_code != STATUS_CODE_200:
98 log_response(response)
101 except requests.exceptions.RequestException
as e:
102 _logger.error(f
"Error calling REST API: {e}")
105 return response.json()
109 """Creates a new concept description. Experimental feature - may not be supported by all servers.
111 :param concept_description: Concept description data as a dictionary
112 :return: True if creation was successful, False otherwise
114 url = f
"{self._client.base_url}/concept-descriptions"
119 response = self._session.post(url, json=concept_description, timeout=self.
_client.time_out)
120 _logger.debug(f
"Call REST API url '{response.url}'")
122 if response.status_code == STATUS_CODE_404:
123 if self.
_client.get_log_intensity() == LogIntensity.HIGH:
124 _logger.warning(
"Concept descriptions endpoint not found.")
125 elif self.
_client.get_log_intensity() == LogIntensity.STANDARD:
126 _logger.debug(
"Concept descriptions endpoint not found.")
128 _logger.debug(response.text)
131 if response.status_code != STATUS_CODE_201:
132 log_response(response)
135 except requests.exceptions.RequestException
as e:
136 _logger.error(f
"Error calling REST API: {e}")
143 """Deletes a specific concept description by its identifier. Experimental feature - may not be supported by all servers.
145 :param concept_description_identifier: The unique identifier of the concept description
146 :return: True if deletion was successful, False otherwise
148 url = f
"{self._client.base_url}/concept-descriptions/{concept_description_identifier}"
153 response = self._session.delete(url, timeout=self.
_client.time_out)
154 _logger.debug(f
"Call REST API url '{response.url}'")
156 if response.status_code == STATUS_CODE_404:
157 if self.
_client.get_log_intensity() == LogIntensity.HIGH:
158 _logger.warning(f
"Concept description with id '{concept_description_identifier}' not found.")
159 elif self.
_client.get_log_intensity() == LogIntensity.STANDARD:
160 _logger.debug(f
"Concept description with id '{concept_description_identifier}' not found.")
162 _logger.debug(response.text)
165 if response.status_code != STATUS_CODE_204:
166 log_response(response)
169 except requests.exceptions.RequestException
as e:
170 _logger.error(f
"Error calling REST API: {e}")
177 """Downloads file content from a specific submodel element from the Submodel at a specified path. Experimental feature - may not be supported by all servers.
179 :param submodel_identifier: The Submodels unique id
180 :param id_short_path: IdShort path to the submodel element (dot-separated)
181 :return: Attachment file data as bytes (octet-stream) or None if an error occurred
183 if not self.
_client.encoded_ids:
184 submodel_identifier = encode_base_64(submodel_identifier)
186 url = f
"{self._client.base_url}/submodels/{submodel_identifier}/submodel-elements/{id_short_path}/attachment"
191 response = self._session.get(url, timeout=self.
_client.time_out)
192 _logger.debug(f
"Call REST API url '{response.url}'")
194 if response.status_code == STATUS_CODE_404:
195 if self.
_client.get_log_intensity() == LogIntensity.HIGH:
197 f
"Submodel with id '{submodel_identifier}' or Submodel element with IDShort path '{id_short_path}' or file content not found."
199 elif self.
_client.get_log_intensity() == LogIntensity.STANDARD:
201 f
"Submodel with id '{submodel_identifier}' or Submodel element with IDShort path '{id_short_path}' or file content not found."
204 _logger.debug(response.text)
207 if response.status_code != STATUS_CODE_200:
208 log_response(response)
211 except requests.exceptions.RequestException
as e:
212 _logger.error(f
"Error calling REST API: {e}")
215 return response.content
219 """Uploads file content to an existing submodel element at a specified path within submodel elements hierarchy. Experimental feature - may not be supported by all servers.
221 :param submodel_identifier: The Submodels unique id
222 :param id_short_path: IdShort path to the submodel element (dot-separated)
223 :param file: Path to the file to upload as attachment
224 :return: Attachment data as bytes or None if an error occurred
226 if file.exists()
is False or not file.is_file():
227 _logger.error(f
"Attachment file '{file}' does not exist.")
230 if not self.
_client.encoded_ids:
231 submodel_identifier = encode_base_64(submodel_identifier)
233 url = f
"{self._client.base_url}/submodels/{submodel_identifier}/submodel-elements/{id_short_path}/attachment"
238 mime_type, _ = mimetypes.guess_type(file)
240 with file.open(
"rb")
as f:
241 files = {
"file": (file.name, f, mime_type
or "application/octet-stream")}
242 response = self._session.post(url, files=files, timeout=self.
_client.time_out)
244 _logger.debug(f
"Call REST API url '{response.url}'")
246 if response.status_code == STATUS_CODE_404:
247 if self.
_client.get_log_intensity() == LogIntensity.HIGH:
248 _logger.warning(f
"Submodel with id '{submodel_identifier}' or Submodel element with IDShort path '{id_short_path}' not found.")
249 elif self.
_client.get_log_intensity() == LogIntensity.STANDARD:
250 _logger.debug(f
"Submodel with id '{submodel_identifier}' or Submodel element with IDShort path '{id_short_path}' not found.")
252 _logger.debug(response.text)
256 if response.status_code
not in (STATUS_CODE_200, STATUS_CODE_204):
257 log_response(response)
260 except requests.exceptions.RequestException
as e:
261 _logger.error(f
"Error call REST API: {e}")
268 self, submodel_identifier: str, id_short_path: str, file_octet_stream: Any, mime_type: str =
"application/octet-stream"
270 """Uploads file content to an existing submodel element at a specified path within submodel elements hierarchy. Experimental feature - may not be supported by all servers.
272 :param submodel_identifier: The Submodels unique id
273 :param id_short_path: IdShort path to the submodel element (dot-separated)
274 :param file_octet_stream: File content as a byte stream
275 :return: Attachment data as bytes or None if an error occurred
277 if file_octet_stream
is None:
278 _logger.error(
"Attachment file stream is None.")
281 if not self.
_client.encoded_ids:
282 submodel_identifier = encode_base_64(submodel_identifier)
284 url = f
"{self._client.base_url}/submodels/{submodel_identifier}/submodel-elements/{id_short_path}/attachment"
289 files = {
"file": (
"attachment", file_octet_stream, mime_type
or "application/octet-stream")}
290 response = self._session.post(url, files=files, timeout=self.
_client.time_out)
292 _logger.debug(f
"Call REST API url '{response.url}'")
294 if response.status_code == STATUS_CODE_404:
295 if self.
_client.get_log_intensity() == LogIntensity.HIGH:
296 _logger.warning(f
"Submodel with id '{submodel_identifier}' or Submodel element with IDShort path '{id_short_path}' not found.")
297 elif self.
_client.get_log_intensity() == LogIntensity.STANDARD:
298 _logger.debug(f
"Submodel with id '{submodel_identifier}' or Submodel element with IDShort path '{id_short_path}' not found.")
300 _logger.debug(response.text)
304 if response.status_code
not in (STATUS_CODE_200, STATUS_CODE_204):
305 log_response(response)
308 except requests.exceptions.RequestException
as e:
309 _logger.error(f
"Error call REST API: {e}")
316 """Uploads file content to an existing submodel element at a specified path within submodel elements hierarchy. Experimental feature - may not be supported by all servers.
318 :param submodel_identifier: The Submodels unique id
319 :param id_short_path: IdShort path to the submodel element (dot-separated)
320 :param file: Path to the file to upload as attachment
321 :return: Attachment data as bytes or None if an error occurred
323 if file.exists()
is False or not file.is_file():
324 _logger.error(f
"Attachment file '{file}' does not exist.")
327 if not self.
_client.encoded_ids:
328 submodel_identifier = encode_base_64(submodel_identifier)
330 url = f
"{self._client.base_url}/submodels/{submodel_identifier}/submodel-elements/{id_short_path}/attachment"
335 mime_type, _ = mimetypes.guess_type(file)
337 with file.open(
"rb")
as f:
338 files = {
"file": (file.name, f, mime_type
or "application/octet-stream")}
339 response = self._session.put(url, files=files, timeout=self.
_client.time_out)
341 _logger.debug(f
"Call REST API url '{response.url}'")
343 if response.status_code == STATUS_CODE_404:
344 if self.
_client.get_log_intensity() == LogIntensity.HIGH:
345 _logger.warning(f
"Submodel with id '{submodel_identifier}' or Submodel element with IDShort path '{id_short_path}' not found.")
346 elif self.
_client.get_log_intensity() == LogIntensity.STANDARD:
347 _logger.debug(f
"Submodel with id '{submodel_identifier}' or Submodel element with IDShort path '{id_short_path}' not found.")
349 _logger.debug(response.text)
353 if response.status_code
not in (STATUS_CODE_200, STATUS_CODE_204):
354 log_response(response)
357 except requests.exceptions.RequestException
as e:
358 _logger.error(f
"Error call REST API: {e}")
365 self, submodel_identifier: str, id_short_path: str, file_octet_stream: Any, mime_type: str =
"application/octet-stream"
367 """Uploads file content to an existing submodel element at a specified path within submodel elements hierarchy. Experimental feature - may not be supported by all servers.
369 :param submodel_identifier: The Submodels unique id
370 :param id_short_path: IdShort path to the submodel element (dot-separated)
371 :param file_octet_stream: File content as a byte stream
372 :return: Attachment data as bytes or None if an error occurred
374 if file_octet_stream
is None:
375 _logger.error(
"Attachment file stream is None.")
378 if not self.
_client.encoded_ids:
379 submodel_identifier = encode_base_64(submodel_identifier)
381 url = f
"{self._client.base_url}/submodels/{submodel_identifier}/submodel-elements/{id_short_path}/attachment"
386 files = {
"file": (
"attachment", file_octet_stream, mime_type
or "application/octet-stream")}
387 response = self._session.put(url, files=files, timeout=self.
_client.time_out)
389 _logger.debug(f
"Call REST API url '{response.url}'")
391 if response.status_code == STATUS_CODE_404:
392 if self.
_client.get_log_intensity() == LogIntensity.HIGH:
393 _logger.warning(f
"Submodel with id '{submodel_identifier}' or Submodel element with IDShort path '{id_short_path}' not found.")
394 elif self.
_client.get_log_intensity() == LogIntensity.STANDARD:
395 _logger.debug(f
"Submodel with id '{submodel_identifier}' or Submodel element with IDShort path '{id_short_path}' not found.")
397 _logger.debug(response.text)
401 if response.status_code
not in (STATUS_CODE_200, STATUS_CODE_204):
402 log_response(response)
405 except requests.exceptions.RequestException
as e:
406 _logger.error(f
"Error call REST API: {e}")
413 """Deletes file content of an existing submodel element at a specified path within submodel elements hierarchy. Experimental feature - may not be supported by all servers.
415 :param submodel_identifier: The Submodels unique id
416 :param id_short_path: IdShort path to the submodel element (dot-separated)
417 :return: True if deletion was successful, False otherwise
419 if not self.
_client.encoded_ids:
420 submodel_identifier = encode_base_64(submodel_identifier)
422 url = f
"{self._client.base_url}/submodels/{submodel_identifier}/submodel-elements/{id_short_path}/attachment"
427 response = self._session.delete(url, timeout=self.
_client.time_out)
428 _logger.debug(f
"Call REST API url '{response.url}'")
430 if response.status_code == 404:
431 if self.
_client.get_log_intensity() == LogIntensity.HIGH:
432 _logger.warning(f
"Submodel with id '{submodel_identifier}' or Submodel element with IDShort path '{id_short_path}' not found.")
433 elif self.
_client.get_log_intensity() == LogIntensity.STANDARD:
434 _logger.debug(f
"Submodel with id '{submodel_identifier}' or Submodel element with IDShort path '{id_short_path}' not found.")
438 if response.status_code != STATUS_CODE_200:
439 log_response(response)
442 except requests.exceptions.RequestException
as e:
443 _logger.error(f
"Error calling REST API: {e}")
448 def __post_multipart(self, url, files) -> requests.Response:
449 headers = dict(self._session.headers)
450 headers.pop(
"Content-Type",
None)
451 return self._session.post(url, headers=headers, files=files)
Implementation of Asset Administration Shell Registry related API calls.
bool delete_file_by_path_submodel_repo(self, str submodel_identifier, str id_short_path)
Deletes file content of an existing submodel element at a specified path within submodel elements hie...
bool post_file_by_path_submodel_repo(self, str submodel_identifier, str id_short_path, Path file)
Uploads file content to an existing submodel element at a specified path within submodel elements hie...
bool post_file_by_path_submodel_repo_stream(self, str submodel_identifier, str id_short_path, Any file_octet_stream, str mime_type="application/octet-stream")
Uploads file content to an existing submodel element at a specified path within submodel elements hie...
bytes|None get_file_by_path_submodel_repo(self, str submodel_identifier, str id_short_path)
Downloads file content from a specific submodel element from the Submodel at a specified path.
get_concept_description_by_id(self, str concept_description_identifier)
Retrieves a specific concept description by its identifier.
bool put_file_by_path_submodel_repo_stream(self, str submodel_identifier, str id_short_path, Any file_octet_stream, str mime_type="application/octet-stream")
Uploads file content to an existing submodel element at a specified path within submodel elements hie...
bool post_concept_description(self, dict concept_description)
Creates a new concept description.
get_concept_descriptions(self)
Retrieves concept descriptions.
__init__(self, "AasHttpClient" client)
Initializes the ExperimentalImplementation with the given client.
bool put_file_by_path_submodel_repo(self, str submodel_identifier, str id_short_path, Path file)
Uploads file content to an existing submodel element at a specified path within submodel elements hie...
bool delete_concept_description_by_id(self, str concept_description_identifier)
Deletes a specific concept description by its identifier.