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 # PUT /shells/{aasIdentifier}/asset-information/thumbnail
177 def put_thumbnail_aas_repository(self, aas_identifier: str, file_name: str, file: Path) -> bool:
178 """Creates or updates the thumbnail of the Asset Administration Shell.
179
180 :param aas_identifier: The Asset Administration Shells unique id
181 :param file_name: The name of the thumbnail file
182 :param file: Path to the thumbnail file to upload as attachment
183 :return: True if the update was successful, False otherwise
184 """
185 if file.exists() is False or not file.is_file():
186 _logger.error(f"Attachment file '{file}' does not exist.")
187 return False
188
189 if not self._client.encoded_ids:
190 aas_identifier = encode_base_64(aas_identifier)
191
192 url = f"{self._client.base_url}/shells/{aas_identifier}/asset-information/thumbnail"
193
194 params = {"fileName": file_name}
195
196 self._client.set_token()
197
198 try:
199 mime_type, _ = mimetypes.guess_type(file)
200
201 with file.open("rb") as f:
202 files = {"file": (file.name, f, mime_type or "application/octet-stream")}
203 response = self._session.put(url, files=files, params=params, timeout=self._client.time_out)
204
205 _logger.debug(f"Call REST API url '{response.url}'")
206
207 if response.status_code == STATUS_CODE_404:
208 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' not found.")
209 _logger.debug(response.text)
210 return False
211
212 # original dotnet server delivers 200 instead of 204
213 if response.status_code not in (STATUS_CODE_200, STATUS_CODE_204):
214 log_response(response)
215 return False
216
217 except requests.exceptions.RequestException as e:
218 _logger.error(f"Error call REST API: {e}")
219 return False
220
221 return True
222
223 # DELETE /shells/{aasIdentifier}/asset-information/thumbnail
224 def delete_thumbnail_aas_repository(self, aas_identifier: str) -> bool:
225 """Deletes the thumbnail of the Asset Administration Shell.
226
227 :param aas_identifier: The Asset Administration Shells unique id
228 :return: True if the deletion was successful, False otherwise
229 """
230 if not self._client.encoded_ids:
231 aas_identifier = encode_base_64(aas_identifier)
233 url = f"{self._client.base_url}/shells/{aas_identifier}/asset-information/thumbnail"
234
235 self._client.set_token()
236
237 try:
238 response = self._session.delete(url, timeout=self._client.time_out)
239 _logger.debug(f"Call REST API url '{response.url}'")
240
241 if response.status_code == STATUS_CODE_404:
242 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' or thumbnail file not found.")
243 _logger.debug(response.text)
244 return False
245
246 if response.status_code != STATUS_CODE_200:
247 log_response(response)
248 return False
249
250 except requests.exceptions.RequestException as e:
251 _logger.error(f"Error call REST API: {e}")
252 return False
253
254 return True
255
256 # GET /shells
258 self, asset_ids: list[dict] | None = None, id_short: str = "", limit: int = 100, cursor: str = ""
259 ) -> dict | None:
260 """Returns all Asset Administration Shells.
261
262 :param assetIds: A list of specific Asset identifiers (format: {"identifier": "string", "encodedIdentifier": "string"})
263 :param idShort: The Asset Administration Shells IdShort
264 :param limit: The maximum number of elements in the response array
265 :param cursor: A server-generated identifier retrieved from pagingMetadata that specifies from which position the result listing should continue
266 :return: List of paginated Asset Administration Shells data or None if an error occurred
267 """
268 url = f"{self._client.base_url}/shells"
269
270 # Build query parameters
271 if asset_ids is None:
272 asset_ids = []
273
274 params: dict[str, Any] = {}
275 if asset_ids is not None and len(asset_ids) > 0:
276 params["assetIds"] = asset_ids
277 if id_short:
278 params["idShort"] = id_short
279 if limit:
280 params["limit"] = str(limit)
281 if cursor:
282 params["cursor"] = cursor
283
284 self._client.set_token()
285
286 try:
287 response = self._session.get(url, timeout=self._client.time_out, params=params)
288 _logger.debug(f"Call REST API url '{response.url}'")
289
290 if response.status_code != STATUS_CODE_200:
291 log_response(response)
292 return None
293
294 except requests.exceptions.RequestException as e:
295 _logger.error(f"Error call REST API: {e}")
296 return None
297
298 content = response.content.decode("utf-8")
299 return json.loads(content)
300
301 # POST /shells
302 def post_asset_administration_shell(self, request_body: dict) -> dict | None:
303 """Creates a new Asset Administration Shell.
304
305 :param request_body: Json data of the Asset Administration Shell to post
306 :return: Response data as a dictionary or None if an error occurred
307 """
308 url = f"{self._client.base_url}/shells"
309
310 self._client.set_token()
311
312 try:
313 response = self._session.post(url, json=request_body, timeout=self._client.time_out)
314 _logger.debug(f"Call REST API url '{response.url}'")
315
316 if response.status_code != STATUS_CODE_201:
317 log_response(response)
318 return None
319
320 except requests.exceptions.RequestException as e:
321 _logger.error(f"Error call REST API: {e}")
322 return None
323
324 content = response.content.decode("utf-8")
325 return json.loads(content)
326
327 # GET /shells/{aasIdentifier}/submodel-refs
328 def get_all_submodel_references_aas_repository(self, aas_identifier: str, limit: int = 100, cursor: str = "") -> dict | None:
329 """Returns all submodel references.
330
331 :param aas_identifier: The Asset Administration Shells unique id
332 :param limit: The maximum number of elements in the response array
333 :param cursor: A server-generated identifier retrieved from pagingMetadata that specifies from which position the result listing should continue
334 :return: List of Submodel references or None if an error occurred
335 """
336 if not self._client.encoded_ids:
337 aas_identifier = encode_base_64(aas_identifier)
339 url = f"{self._client.base_url}/shells/{aas_identifier}/submodel-refs"
340
341 params: dict[str, str] = {}
342 if limit:
343 params["limit"] = str(limit)
344 if cursor:
345 params["cursor"] = cursor
346
347 self._client.set_token()
348
349 try:
350 response = self._session.get(url, timeout=self._client.time_out, params=params)
351 _logger.debug(f"Call REST API url '{response.url}'")
352
353 if response.status_code == STATUS_CODE_404:
354 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' not found.")
355 _logger.debug(response.text)
356 return None
357
358 if response.status_code != STATUS_CODE_200:
359 log_response(response)
360 return None
361
362 except requests.exceptions.RequestException as e:
363 _logger.error(f"Error call REST API: {e}")
364 return None
365
366 content = response.content.decode("utf-8")
367 return json.loads(content)
368
369 # POST /shells/{aasIdentifier}/submodel-refs
370 def post_submodel_reference_aas_repository(self, aas_identifier: str, request_body: dict) -> dict | None:
371 """Creates a submodel reference at the Asset Administration Shell.
372
373 :param aas_identifier: The Asset Administration Shells unique id
374 :param request_body: Reference to the Submodel
375 :return: Response data as a dictionary or None if an error occurred
376 """
377 if not self._client.encoded_ids:
378 aas_identifier = encode_base_64(aas_identifier)
380 url = f"{self._client.base_url}/shells/{aas_identifier}/submodel-refs"
381
382 self._client.set_token()
383
384 try:
385 response = self._session.post(url, json=request_body, timeout=self._client.time_out)
386 _logger.debug(f"Call REST API url '{response.url}'")
387
388 if response.status_code == STATUS_CODE_404:
389 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' not found.")
390 _logger.debug(response.text)
391 return None
392
393 if response.status_code != STATUS_CODE_201:
394 log_response(response)
395 return None
396
397 except requests.exceptions.RequestException as e:
398 _logger.error(f"Error call REST API: {e}")
399 return None
400
401 content = response.content.decode("utf-8")
402 return json.loads(content)
403
404 # DELETE /shells/{aasIdentifier}/submodel-refs/{submodelIdentifier}
405 def delete_submodel_reference_by_id_aas_repository(self, aas_identifier: str, submodel_identifier: str) -> bool:
406 """Deletes the submodel reference from the Asset Administration Shell. Does not delete the submodel itself.
407
408 :param aas_identifier: The Asset Administration Shells unique id
409 :param submodel_identifier: The Submodels unique id
410 :return: True if the deletion was successful, False otherwise
411 """
412 if not self._client.encoded_ids:
413 aas_identifier = encode_base_64(aas_identifier)
414 submodel_identifier = encode_base_64(submodel_identifier)
415
416 url = f"{self._client.base_url}/shells/{aas_identifier}/submodel-refs/{submodel_identifier}"
417
418 self._client.set_token()
419
420 try:
421 response = self._session.delete(url, timeout=self._client.time_out)
422 _logger.debug(f"Call REST API url '{response.url}'")
423
424 if response.status_code == STATUS_CODE_404:
425 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' or submodel with id '{submodel_identifier}' not found.")
426 _logger.debug(response.text)
427 return False
428
429 if response.status_code not in (STATUS_CODE_204, STATUS_CODE_200):
430 log_response(response)
431 return False
432
433 except requests.exceptions.RequestException as e:
434 _logger.error(f"Error call REST API: {e}")
435 return False
436
437 return True
438
439 # not supported by Java Server
440
441 # PUT /shells/{aasIdentifier}/submodels/{submodelIdentifier}
442 def put_submodel_by_id_aas_repository(self, aas_identifier: str, submodel_identifier: str, request_body: dict) -> bool:
443 """Updates the Submodel.
444
445 :param aas_identifier: ID of the AAS to update the submodel for
446 :param submodel_identifier: ID of the submodel to update
447 :param request_body: Json data to the Submodel to put
448 :return: True if the update was successful, False otherwise
449 """
450 if not self._client.encoded_ids:
451 aas_identifier = encode_base_64(aas_identifier)
452 submodel_identifier = encode_base_64(submodel_identifier)
453
454 url = f"{self._client.base_url}/shells/{aas_identifier}/submodels/{submodel_identifier}"
455
456 self._client.set_token()
457
458 try:
459 response = self._session.put(url, json=request_body, timeout=self._client.time_out)
460 _logger.debug(f"Call REST API url '{response.url}'")
461
462 if response.status_code == STATUS_CODE_404:
463 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' or submodel with id '{submodel_identifier}' not found.")
464 _logger.debug(response.text)
465 return False
466
467 if response.status_code != STATUS_CODE_204:
468 log_response(response)
469 return False
470
471 except requests.exceptions.RequestException as e:
472 _logger.error(f"Error call REST API: {e}")
473 return False
474
475 return True
476
477 # GET /shells/{aasIdentifier}/$reference
478 def get_asset_administration_shell_by_id_reference_aas_repository(self, aas_identifier: str) -> dict | None:
479 """Returns a specific Asset Administration Shell as a Reference.
480
481 :param aas_identifier: ID of the AAS reference to retrieve
482 :return: Asset Administration Shells reference data or None if an error occurred
483 """
484 if not self._client.encoded_ids:
485 aas_identifier = encode_base_64(aas_identifier)
487 url = f"{self._client.base_url}/shells/{aas_identifier}/$reference"
488
489 self._client.set_token()
490
491 try:
492 response = self._session.get(url, timeout=self._client.time_out)
493 _logger.debug(f"Call REST API url '{response.url}'")
494
495 if response.status_code == STATUS_CODE_404:
496 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' not found.")
497 _logger.debug(response.text)
498 return None
499
500 if response.status_code != STATUS_CODE_200:
501 log_response(response)
502 return None
503
504 except requests.exceptions.RequestException as e:
505 _logger.error(f"Error call REST API: {e}")
506 return None
507
508 ref_dict_string = response.content.decode("utf-8")
509 return json.loads(ref_dict_string)
510
511 # GET /shells/{aasIdentifier}/submodels/{submodelIdentifier}
512 def get_submodel_by_id_aas_repository(self, aas_identifier: str, submodel_identifier: str) -> dict | None:
513 """Returns the Submodel.
514
515 :param aas_identifier: ID of the AAS to retrieve the submodel from
516 :param submodel_identifier: ID of the submodel to retrieve
517 :return: Submodel object or None if an error occurred
518 """
519 if not self._client.encoded_ids:
520 aas_identifier = encode_base_64(aas_identifier)
521 submodel_identifier = encode_base_64(submodel_identifier)
522
523 url = f"{self._client.base_url}/shells/{aas_identifier}/submodels/{submodel_identifier}"
524
525 self._client.set_token()
526
527 try:
528 response = self._session.get(url, timeout=self._client.time_out)
529 _logger.debug(f"Call REST API url '{response.url}'")
530
531 if response.status_code == STATUS_CODE_404:
532 _logger.warning(f"Asset Administration Shell with id '{aas_identifier}' or submodel with id '{submodel_identifier}' not found.")
533 _logger.debug(response.text)
534 return None
535
536 if response.status_code != STATUS_CODE_200:
537 log_response(response)
538 return None
539
540 except requests.exceptions.RequestException as e:
541 _logger.error(f"Error call REST API: {e}")
542 return None
543
544 content = response.content.decode("utf-8")
545 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.
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.