AAS HTTP Client Documentation
Loading...
Searching...
No Matches
shell_implementation.py
Go to the documentation of this file.
1"""Implementation of Asset Administration Shell related API calls."""
2
3import json
4import logging
5import mimetypes
6from pathlib import Path
7from typing import TYPE_CHECKING, Any
8
9import requests
10from pydantic import BaseModel
11
12if TYPE_CHECKING:
13 from aas_http_client.classes.client.aas_client import AasHttpClient
14
15from aas_http_client.utilities.encoder import encode_base_64
17 STATUS_CODE_200,
18 STATUS_CODE_201,
19 STATUS_CODE_204,
20 STATUS_CODE_404,
21 log_response,
22)
23
24_logger = logging.getLogger(__name__)
25
26
27class ShellRepoImplementation(BaseModel):
28 """Implementation of Asset Administration Shell related API calls."""
29
30 def __init__(self, client: "AasHttpClient"):
31 """Initializes the ShellImplementation with the given parameters."""
32 self._client = client
33
34 session = client.get_session()
35 if session is None:
36 raise ValueError(
37 "HTTP session is not initialized in the client. Call 'initialize()' method of the client before creating SubmodelRegistryImplementation instance."
38 )
39
40 self._session: requests.Session = session
41
42 # GET /shells/{aasIdentifier}
43 def get_asset_administration_shell_by_id(self, aas_identifier: str) -> dict | None:
44 """Returns a specific Asset Administration Shell.
45
46 :param aas_identifier: The Asset Administration Shells unique id
47 :return: Asset Administration Shells data or None if an error occurred
48 """
49 if not self._client.encoded_ids:
50 aas_identifier = encode_base_64(aas_identifier)
52 url = f"{self._client.base_url}/shells/{aas_identifier}"
53
54 self._client.set_token()
55
56 try:
57 response = self._session.get(url, timeout=self._client.time_out)
58 _logger.debug(f"Call REST API url '{response.url}'")
59
60 if response.status_code == STATUS_CODE_404:
61 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' not found.")
62 _logger.debug(response.text)
63 return None
64
65 if response.status_code != STATUS_CODE_200:
66 log_response(response)
67 return None
68
69 except requests.exceptions.RequestException as e:
70 _logger.error(f"Error call REST API: {e}")
71 return None
72
73 content = response.content.decode("utf-8")
74 return json.loads(content)
75
76 # PUT /shells/{aasIdentifier}
77 def put_asset_administration_shell_by_id(self, aas_identifier: str, request_body: dict) -> bool:
78 """Creates or replaces an existing Asset Administration Shell.
79
80 :param aas_identifier: The Asset Administration Shells unique id
81 :param request_body: Json data of the Asset Administration Shell data to put
82 :return: True if the update was successful, False otherwise
83 """
84 if not self._client.encoded_ids:
85 aas_identifier = encode_base_64(aas_identifier)
87 url = f"{self._client.base_url}/shells/{aas_identifier}"
88
89 self._client.set_token()
90
91 try:
92 response = self._session.put(url, json=request_body, timeout=self._client.time_out)
93 _logger.debug(f"Call REST API url '{response.url}'")
94
95 if response.status_code == STATUS_CODE_404:
96 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' not found.")
97 _logger.debug(response.text)
98 return False
99
100 if response.status_code is not STATUS_CODE_204:
101 log_response(response)
102 return False
103
104 except requests.exceptions.RequestException as e:
105 _logger.error(f"Error call REST API: {e}")
106 return False
107
108 return True
109
110 # DELETE /shells/{aasIdentifier}
111 def delete_asset_administration_shell_by_id(self, aas_identifier: str) -> bool:
112 """Deletes an Asset Administration Shell.
113
114 :param aas_identifier: The Asset Administration Shells unique id
115 :return: True if the deletion was successful, False otherwise
116 """
117 if not self._client.encoded_ids:
118 aas_identifier = encode_base_64(aas_identifier)
120 url = f"{self._client.base_url}/shells/{aas_identifier}"
121
122 self._client.set_token()
123
124 try:
125 response = self._session.delete(url, timeout=self._client.time_out)
126 _logger.debug(f"Call REST API url '{response.url}'")
127
128 if response.status_code == STATUS_CODE_404:
129 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' not found.")
130 _logger.debug(response.text)
131 return False
132
133 if response.status_code != STATUS_CODE_204:
134 log_response(response)
135 return False
136
137 except requests.exceptions.RequestException as e:
138 _logger.error(f"Error call REST API: {e}")
139 return False
140
141 return True
142
143 # GET /shells/{aasIdentifier}/asset-information/thumbnail
144 def get_thumbnail_aas_repository(self, aas_identifier: str) -> bytes | None:
145 """Returns the thumbnail of the Asset Administration Shell.
146
147 :param aas_identifier: The Asset Administration Shells unique id
148 :return: Thumbnail file data as bytes (octet-stream) or None if an error occurred
149 """
150 if not self._client.encoded_ids:
151 aas_identifier = encode_base_64(aas_identifier)
153 url = f"{self._client.base_url}/shells/{aas_identifier}/asset-information/thumbnail"
154
155 self._client.set_token()
156
157 try:
158 response = self._session.get(url, timeout=self._client.time_out)
159 _logger.debug(f"Call REST API url '{response.url}'")
160
161 if response.status_code == STATUS_CODE_404:
162 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' or thumbnail file not found.")
163 _logger.debug(response.text)
164 return None
165
166 if response.status_code != STATUS_CODE_200:
167 log_response(response)
168 return None
169
170 except requests.exceptions.RequestException as e:
171 _logger.error(f"Error call REST API: {e}")
172 return None
173
174 return response.content
175
176 def put_thumbnail_aas_repository(self, aas_identifier: str, file_name: str, file: Path) -> bool:
177 """Creates or updates the thumbnail of the Asset Administration Shell.
178
179 :param aas_identifier: The Asset Administration Shells unique id
180 :param file_name: The name of the thumbnail file
181 :param file: Path to the thumbnail file to upload as attachment
182 :return: True if the update was successful, False otherwise
183 """
184 if file.exists() is False or not file.is_file():
185 _logger.error(f"Attachment file '{file}' does not exist.")
186 return False
187
188 mime_type, _ = mimetypes.guess_type(file)
189
190 with file.open("rb") as f:
191 file_octet_stream = f.read()
192
193 return self.put_thumbnail_aas_repository_stream(aas_identifier, file_name, file_octet_stream, mime_type or "application/octet-stream")
194
195 # PUT /shells/{aasIdentifier}/asset-information/thumbnail
197 self, aas_identifier: str, file_name: str, file_octet_stream: Any, mime_type: str = "application/octet-stream"
198 ) -> bool:
199 """Creates or updates the thumbnail of the Asset Administration Shell.
200
201 :param aas_identifier: The Asset Administration Shells unique id
202 :param file_name: The name of the thumbnail file
203 :param file_octet_stream: The octet stream of the thumbnail file
204 :param mime_type: The MIME type of the thumbnail file (e.g., "image/png")
205 :return: True if the update was successful, False otherwise
206 """
207 if file_name is None or file_name == "" or file_octet_stream is None or mime_type is None or mime_type == "":
208 _logger.error(f"Attachment file '{file_name}' does not exist.")
209 return False
210
211 if not self._client.encoded_ids:
212 aas_identifier = encode_base_64(aas_identifier)
213
214 url = f"{self._client.base_url}/shells/{aas_identifier}/asset-information/thumbnail"
215
216 params = {"fileName": file_name}
217
218 self._client.set_token()
219
220 try:
221 files: dict[str, tuple[str, Any, str]] = {"file": (file_name, file_octet_stream, mime_type)}
222 response = self._session.put(url, files=files, params=params, timeout=self._client.time_out)
223
224 _logger.debug(f"Call REST API url '{response.url}'")
225
226 if response.status_code == STATUS_CODE_404:
227 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' not found.")
228 _logger.debug(response.text)
229 return False
230
231 # original dotnet server delivers 200 instead of 204
232 if response.status_code not in (STATUS_CODE_200, STATUS_CODE_204):
233 log_response(response)
234 return False
235
236 except requests.exceptions.RequestException as e:
237 _logger.error(f"Error call REST API: {e}")
238 return False
239
240 return True
241
242 # DELETE /shells/{aasIdentifier}/asset-information/thumbnail
243 def delete_thumbnail_aas_repository(self, aas_identifier: str) -> bool:
244 """Deletes the thumbnail of the Asset Administration Shell.
245
246 :param aas_identifier: The Asset Administration Shells unique id
247 :return: True if the deletion was successful, False otherwise
248 """
249 if not self._client.encoded_ids:
250 aas_identifier = encode_base_64(aas_identifier)
252 url = f"{self._client.base_url}/shells/{aas_identifier}/asset-information/thumbnail"
253
254 self._client.set_token()
255
256 try:
257 response = self._session.delete(url, timeout=self._client.time_out)
258 _logger.debug(f"Call REST API url '{response.url}'")
259
260 if response.status_code == STATUS_CODE_404:
261 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' or thumbnail file not found.")
262 _logger.debug(response.text)
263 return False
264
265 if response.status_code != STATUS_CODE_200:
266 log_response(response)
267 return False
268
269 except requests.exceptions.RequestException as e:
270 _logger.error(f"Error call REST API: {e}")
271 return False
272
273 return True
274
275 # GET /shells
277 self, asset_ids: list[dict] | None = None, id_short: str = "", limit: int = 100, cursor: str = ""
278 ) -> dict | None:
279 """Returns all Asset Administration Shells.
280
281 :param assetIds: A list of specific Asset identifiers (format: {"identifier": "string", "encodedIdentifier": "string"})
282 :param idShort: The Asset Administration Shells IdShort
283 :param limit: The maximum number of elements in the response array
284 :param cursor: A server-generated identifier retrieved from pagingMetadata that specifies from which position the result listing should continue
285 :return: List of paginated Asset Administration Shells data or None if an error occurred
286 """
287 url = f"{self._client.base_url}/shells"
288
289 # Build query parameters
290 if asset_ids is None:
291 asset_ids = []
292
293 params: dict[str, Any] = {}
294 if asset_ids is not None and len(asset_ids) > 0:
295 params["assetIds"] = asset_ids
296 if id_short:
297 params["idShort"] = id_short
298 if limit:
299 params["limit"] = str(limit)
300 if cursor:
301 params["cursor"] = cursor
302
303 self._client.set_token()
304
305 try:
306 response = self._session.get(url, timeout=self._client.time_out, params=params)
307 _logger.debug(f"Call REST API url '{response.url}'")
308
309 if response.status_code != STATUS_CODE_200:
310 log_response(response)
311 return None
312
313 except requests.exceptions.RequestException as e:
314 _logger.error(f"Error call REST API: {e}")
315 return None
316
317 content = response.content.decode("utf-8")
318 return json.loads(content)
319
320 # POST /shells
321 def post_asset_administration_shell(self, request_body: dict) -> dict | None:
322 """Creates a new Asset Administration Shell.
323
324 :param request_body: Json data of the Asset Administration Shell to post
325 :return: Response data as a dictionary or None if an error occurred
326 """
327 url = f"{self._client.base_url}/shells"
328
329 self._client.set_token()
330
331 try:
332 response = self._session.post(url, json=request_body, timeout=self._client.time_out)
333 _logger.debug(f"Call REST API url '{response.url}'")
334
335 if response.status_code != STATUS_CODE_201:
336 log_response(response)
337 return None
338
339 except requests.exceptions.RequestException as e:
340 _logger.error(f"Error call REST API: {e}")
341 return None
342
343 content = response.content.decode("utf-8")
344 return json.loads(content)
345
346 # GET /shells/{aasIdentifier}/submodel-refs
347 def get_all_submodel_references_aas_repository(self, aas_identifier: str, limit: int = 100, cursor: str = "") -> dict | None:
348 """Returns all submodel references.
349
350 :param aas_identifier: The Asset Administration Shells unique id
351 :param limit: The maximum number of elements in the response array
352 :param cursor: A server-generated identifier retrieved from pagingMetadata that specifies from which position the result listing should continue
353 :return: List of Submodel references or None if an error occurred
354 """
355 if not self._client.encoded_ids:
356 aas_identifier = encode_base_64(aas_identifier)
358 url = f"{self._client.base_url}/shells/{aas_identifier}/submodel-refs"
359
360 params: dict[str, str] = {}
361 if limit:
362 params["limit"] = str(limit)
363 if cursor:
364 params["cursor"] = cursor
365
366 self._client.set_token()
367
368 try:
369 response = self._session.get(url, timeout=self._client.time_out, params=params)
370 _logger.debug(f"Call REST API url '{response.url}'")
371
372 if response.status_code == STATUS_CODE_404:
373 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' not found.")
374 _logger.debug(response.text)
375 return None
376
377 if response.status_code != STATUS_CODE_200:
378 log_response(response)
379 return None
380
381 except requests.exceptions.RequestException as e:
382 _logger.error(f"Error call REST API: {e}")
383 return None
384
385 content = response.content.decode("utf-8")
386 return json.loads(content)
387
388 # POST /shells/{aasIdentifier}/submodel-refs
389 def post_submodel_reference_aas_repository(self, aas_identifier: str, request_body: dict) -> dict | None:
390 """Creates a submodel reference at the Asset Administration Shell.
391
392 :param aas_identifier: The Asset Administration Shells unique id
393 :param request_body: Reference to the Submodel
394 :return: Response data as a dictionary or None if an error occurred
395 """
396 if not self._client.encoded_ids:
397 aas_identifier = encode_base_64(aas_identifier)
399 url = f"{self._client.base_url}/shells/{aas_identifier}/submodel-refs"
400
401 self._client.set_token()
402
403 try:
404 response = self._session.post(url, json=request_body, timeout=self._client.time_out)
405 _logger.debug(f"Call REST API url '{response.url}'")
406
407 if response.status_code == STATUS_CODE_404:
408 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' not found.")
409 _logger.debug(response.text)
410 return None
411
412 if response.status_code != STATUS_CODE_201:
413 log_response(response)
414 return None
415
416 except requests.exceptions.RequestException as e:
417 _logger.error(f"Error call REST API: {e}")
418 return None
419
420 content = response.content.decode("utf-8")
421 return json.loads(content)
422
423 # DELETE /shells/{aasIdentifier}/submodel-refs/{submodelIdentifier}
424 def delete_submodel_reference_by_id_aas_repository(self, aas_identifier: str, submodel_identifier: str) -> bool:
425 """Deletes the submodel reference from the Asset Administration Shell. Does not delete the submodel itself.
426
427 :param aas_identifier: The Asset Administration Shells unique id
428 :param submodel_identifier: The Submodels unique id
429 :return: True if the deletion was successful, False otherwise
430 """
431 if not self._client.encoded_ids:
432 aas_identifier = encode_base_64(aas_identifier)
433 submodel_identifier = encode_base_64(submodel_identifier)
434
435 url = f"{self._client.base_url}/shells/{aas_identifier}/submodel-refs/{submodel_identifier}"
436
437 self._client.set_token()
438
439 try:
440 response = self._session.delete(url, timeout=self._client.time_out)
441 _logger.debug(f"Call REST API url '{response.url}'")
442
443 if response.status_code == STATUS_CODE_404:
444 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' or submodel with id '{submodel_identifier}' not found.")
445 _logger.debug(response.text)
446 return False
447
448 if response.status_code not in (STATUS_CODE_204, STATUS_CODE_200):
449 log_response(response)
450 return False
451
452 except requests.exceptions.RequestException as e:
453 _logger.error(f"Error call REST API: {e}")
454 return False
455
456 return True
457
458 # not supported by Java Server
459
460 # PUT /shells/{aasIdentifier}/submodels/{submodelIdentifier}
461 def put_submodel_by_id_aas_repository(self, aas_identifier: str, submodel_identifier: str, request_body: dict) -> bool:
462 """Updates the Submodel.
463
464 :param aas_identifier: ID of the AAS to update the submodel for
465 :param submodel_identifier: ID of the submodel to update
466 :param request_body: Json data to the Submodel to put
467 :return: True if the update was successful, False otherwise
468 """
469 if not self._client.encoded_ids:
470 aas_identifier = encode_base_64(aas_identifier)
471 submodel_identifier = encode_base_64(submodel_identifier)
472
473 url = f"{self._client.base_url}/shells/{aas_identifier}/submodels/{submodel_identifier}"
474
475 self._client.set_token()
476
477 try:
478 response = self._session.put(url, json=request_body, timeout=self._client.time_out)
479 _logger.debug(f"Call REST API url '{response.url}'")
480
481 if response.status_code == STATUS_CODE_404:
482 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' or submodel with id '{submodel_identifier}' not found.")
483 _logger.debug(response.text)
484 return False
485
486 if response.status_code != STATUS_CODE_204:
487 log_response(response)
488 return False
489
490 except requests.exceptions.RequestException as e:
491 _logger.error(f"Error call REST API: {e}")
492 return False
493
494 return True
495
496 # GET /shells/{aasIdentifier}/$reference
497 def get_asset_administration_shell_by_id_reference_aas_repository(self, aas_identifier: str) -> dict | None:
498 """Returns a specific Asset Administration Shell as a Reference.
499
500 :param aas_identifier: ID of the AAS reference to retrieve
501 :return: Asset Administration Shells reference data or None if an error occurred
502 """
503 if not self._client.encoded_ids:
504 aas_identifier = encode_base_64(aas_identifier)
506 url = f"{self._client.base_url}/shells/{aas_identifier}/$reference"
507
508 self._client.set_token()
509
510 try:
511 response = self._session.get(url, timeout=self._client.time_out)
512 _logger.debug(f"Call REST API url '{response.url}'")
513
514 if response.status_code == STATUS_CODE_404:
515 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' not found.")
516 _logger.debug(response.text)
517 return None
518
519 if response.status_code != STATUS_CODE_200:
520 log_response(response)
521 return None
522
523 except requests.exceptions.RequestException as e:
524 _logger.error(f"Error call REST API: {e}")
525 return None
526
527 ref_dict_string = response.content.decode("utf-8")
528 return json.loads(ref_dict_string)
529
530 # GET /shells/{aasIdentifier}/submodels/{submodelIdentifier}
531 def get_submodel_by_id_aas_repository(self, aas_identifier: str, submodel_identifier: str) -> dict | None:
532 """Returns the Submodel.
533
534 :param aas_identifier: ID of the AAS to retrieve the submodel from
535 :param submodel_identifier: ID of the submodel to retrieve
536 :return: Submodel object or None if an error occurred
537 """
538 if not self._client.encoded_ids:
539 aas_identifier = encode_base_64(aas_identifier)
540 submodel_identifier = encode_base_64(submodel_identifier)
541
542 url = f"{self._client.base_url}/shells/{aas_identifier}/submodels/{submodel_identifier}"
543
544 self._client.set_token()
545
546 try:
547 response = self._session.get(url, timeout=self._client.time_out)
548 _logger.debug(f"Call REST API url '{response.url}'")
549
550 if response.status_code == STATUS_CODE_404:
551 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' or submodel with id '{submodel_identifier}' not found.")
552 _logger.debug(response.text)
553 return None
554
555 if response.status_code != STATUS_CODE_200:
556 log_response(response)
557 return None
558
559 except requests.exceptions.RequestException as e:
560 _logger.error(f"Error call REST API: {e}")
561 return None
562
563 content = response.content.decode("utf-8")
564 return json.loads(content)
Implementation of Asset Administration Shell related API calls.
bool delete_thumbnail_aas_repository(self, str aas_identifier)
Deletes the thumbnail of the Asset Administration Shell.
dict|None post_asset_administration_shell(self, dict request_body)
Creates a new Asset Administration Shell.
dict|None get_all_submodel_references_aas_repository(self, str aas_identifier, int limit=100, str cursor="")
Returns all submodel references.
dict|None get_asset_administration_shell_by_id(self, str aas_identifier)
Returns a specific Asset Administration Shell.
__init__(self, "AasHttpClient" client)
Initializes the ShellImplementation with the given parameters.
bytes|None get_thumbnail_aas_repository(self, str aas_identifier)
Returns the thumbnail of the Asset Administration Shell.
dict|None get_asset_administration_shell_by_id_reference_aas_repository(self, str aas_identifier)
Returns a specific Asset Administration Shell as a Reference.
dict|None get_all_asset_administration_shells(self, list[dict]|None asset_ids=None, str id_short="", int limit=100, str cursor="")
Returns all Asset Administration Shells.
bool put_submodel_by_id_aas_repository(self, str aas_identifier, str submodel_identifier, dict request_body)
Updates the Submodel.
bool delete_asset_administration_shell_by_id(self, str aas_identifier)
Deletes an Asset Administration Shell.
dict|None get_submodel_by_id_aas_repository(self, str aas_identifier, str submodel_identifier)
Returns the Submodel.
bool put_thumbnail_aas_repository_stream(self, str aas_identifier, str file_name, Any file_octet_stream, str mime_type="application/octet-stream")
Creates or updates the thumbnail of the Asset Administration Shell.
dict|None post_submodel_reference_aas_repository(self, str aas_identifier, dict request_body)
Creates a submodel reference at the Asset Administration Shell.
bool delete_submodel_reference_by_id_aas_repository(self, str aas_identifier, str submodel_identifier)
Deletes the submodel reference from the Asset Administration Shell.
bool put_asset_administration_shell_by_id(self, str aas_identifier, dict request_body)
Creates or replaces an existing Asset Administration Shell.
bool put_thumbnail_aas_repository(self, str aas_identifier, str file_name, Path file)
Creates or updates the thumbnail of the Asset Administration Shell.