aas-http-client 0.2.5__tar.gz → 0.2.61__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of aas-http-client might be problematic. Click here for more details.

Files changed (22) hide show
  1. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/PKG-INFO +1 -1
  2. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/aas_http_client/client.py +15 -8
  3. aas_http_client-0.2.61/aas_http_client/demo/demo_process.py +95 -0
  4. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/aas_http_client/utilities/model_builder.py +18 -24
  5. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/aas_http_client/wrapper/sdk_wrapper.py +48 -38
  6. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/aas_http_client.egg-info/PKG-INFO +1 -1
  7. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/aas_http_client.egg-info/SOURCES.txt +3 -1
  8. aas_http_client-0.2.61/pyproject.toml +22 -0
  9. aas_http_client-0.2.61/tests/test_client.py +326 -0
  10. aas_http_client-0.2.61/tests/test_wrapper.py +308 -0
  11. aas_http_client-0.2.5/aas_http_client/demo/demo_process.py +0 -93
  12. aas_http_client-0.2.5/pyproject.toml +0 -17
  13. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/LICENSE +0 -0
  14. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/README.md +0 -0
  15. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/aas_http_client/__init__.py +0 -0
  16. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/aas_http_client/core/encoder.py +0 -0
  17. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/aas_http_client/core/version_check.py +0 -0
  18. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/aas_http_client/demo/logging_handler.py +0 -0
  19. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/aas_http_client/utilities/__init__.py +0 -0
  20. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/aas_http_client.egg-info/dependency_links.txt +0 -0
  21. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/aas_http_client.egg-info/top_level.txt +0 -0
  22. {aas_http_client-0.2.5 → aas_http_client-0.2.61}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aas-http-client
3
- Version: 0.2.5
3
+ Version: 0.2.61
4
4
  Summary: Generic python HTTP client for communication with various types of AAS servers
5
5
  Author-email: Daniel Klein <daniel.klein@em.ag>
6
6
  License: # :em engineering methods AG Software License
@@ -1,4 +1,5 @@
1
1
  """Client for HTTP API communication with AAS server."""
2
+
2
3
  import json
3
4
  import logging
4
5
  import time
@@ -8,12 +9,13 @@ import basyx.aas.adapter.json
8
9
  import basyx.aas.adapter.json.json_serialization as js
9
10
  import requests
10
11
  from basyx.aas.model import Reference, Submodel
11
- from aas_http_client.core.encoder import decode_base_64
12
12
  from pydantic import BaseModel, PrivateAttr, ValidationError
13
13
  from requests import Session
14
14
  from requests.auth import HTTPBasicAuth
15
15
  from requests.models import Response
16
16
 
17
+ from aas_http_client.core.encoder import decode_base_64
18
+
17
19
  logger = logging.getLogger(__name__)
18
20
 
19
21
  STATUS_CODE_200 = 200
@@ -55,7 +57,7 @@ def log_response_errors(response: Response):
55
57
  result_error_messages.append(str(message))
56
58
  elif "error" in response_content_dict:
57
59
  result_error_messages.append(response_content_dict.get("error", ""))
58
-
60
+
59
61
  if len(result_error_messages) == 0 and response.text:
60
62
  result_error_messages.append(response.text)
61
63
 
@@ -121,7 +123,7 @@ class AasHttpClient(BaseModel):
121
123
  content = response.content.decode("utf-8")
122
124
  return json.loads(content)
123
125
 
124
- # region shells
126
+ # region shells
125
127
 
126
128
  def post_asset_administration_shell(self, aas_data: dict) -> dict | None:
127
129
  """Creates a new Asset Administration Shell.
@@ -242,7 +244,6 @@ class AasHttpClient(BaseModel):
242
244
  content = response.content.decode("utf-8")
243
245
  return json.loads(content)
244
246
 
245
-
246
247
  def get_asset_administration_shell_by_id_reference_aas_repository(self, aas_id: str) -> Reference | None:
247
248
  """Returns a specific Asset Administration Shell as a Reference.
248
249
 
@@ -278,7 +279,7 @@ class AasHttpClient(BaseModel):
278
279
  decoded_submodel_id: str = decode_base_64(submodel_id)
279
280
 
280
281
  url = f"{self.base_url}/shells/{decoded_aas_id}/submodels/{decoded_submodel_id}"
281
- #/shells/{aasIdentifier}/submodels/{submodelIdentifier}
282
+ # /shells/{aasIdentifier}/submodels/{submodelIdentifier}
282
283
 
283
284
  try:
284
285
  response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
@@ -318,9 +319,9 @@ class AasHttpClient(BaseModel):
318
319
 
319
320
  return True
320
321
 
321
- # endregion
322
+ # endregion
322
323
 
323
- # region submodels
324
+ # region submodels
324
325
 
325
326
  def post_submodel(self, submodel_data: dict) -> dict | None:
326
327
  """Creates a new Submodel.
@@ -509,10 +510,12 @@ class AasHttpClient(BaseModel):
509
510
  content = response.content.decode("utf-8")
510
511
  return json.loads(content)
511
512
 
513
+
512
514
  # endregion
513
515
 
514
516
  # region client
515
517
 
518
+
516
519
  def create_client_by_url(
517
520
  base_url: str,
518
521
  username: str = "",
@@ -547,6 +550,7 @@ def create_client_by_url(
547
550
  config_string = json.dumps(config_dict, indent=4)
548
551
  return _create_client(config_string, password)
549
552
 
553
+
550
554
  def create_client_by_config(config_file: Path, password: str = "") -> AasHttpClient | None:
551
555
  """Create a AAS HTTP client from the given parameters.
552
556
 
@@ -565,6 +569,7 @@ def create_client_by_config(config_file: Path, password: str = "") -> AasHttpCli
565
569
 
566
570
  return _create_client(config_string, password)
567
571
 
572
+
568
573
  def _create_client(config_string: str, password) -> AasHttpClient | None:
569
574
  try:
570
575
  connection_settings = AasHttpClient.model_validate_json(config_string)
@@ -590,6 +595,7 @@ def _create_client(config_string: str, password) -> AasHttpClient | None:
590
595
 
591
596
  return client
592
597
 
598
+
593
599
  def _connect_to_api(client: AasHttpClient) -> bool:
594
600
  start_time = time.time()
595
601
  logger.debug(f"Try to connect to REST API '{client.base_url}' for {client.connection_time_out} seconds")
@@ -609,4 +615,5 @@ def _connect_to_api(client: AasHttpClient) -> bool:
609
615
  logger.warning(f"Retrying connection (attempt: {counter})")
610
616
  time.sleep(1)
611
617
 
612
- # endregion
618
+
619
+ # endregion
@@ -0,0 +1,95 @@
1
+ """Main process for the demo."""
2
+
3
+ import logging
4
+ from pathlib import Path
5
+
6
+ from basyx.aas import model
7
+
8
+ from aas_http_client.client import AasHttpClient, create_client_by_config
9
+ from aas_http_client.utilities import model_builder
10
+ from aas_http_client.wrapper.sdk_wrapper import SdkWrapper, create_wrapper_by_config
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ def start() -> None:
16
+ """Start the demo process."""
17
+ # create a submodel element
18
+ sme_short_id: str = model_builder.create_unique_short_id("poc_sme")
19
+ sme = model_builder.create_base_submodel_element_Property(sme_short_id, model.datatypes.String, "Sample Value")
20
+
21
+ # create a submodel
22
+ sm_short_id: str = model_builder.create_unique_short_id("poc_sm")
23
+ submodel = model_builder.create_base_submodel(sm_short_id)
24
+ # add submodel element to submodel
25
+ # submodel.submodel_element.add(sme)
26
+
27
+ # create an AAS
28
+ aas_short_id: str = model_builder.create_unique_short_id("poc_aas")
29
+ aas = model_builder.create_base_ass(aas_short_id)
30
+
31
+ # add submodel to AAS
32
+ model_builder.add_submodel_to_aas(aas, submodel)
33
+
34
+ wrapper = _create_sdk_wrapper(Path("./aas_http_client/demo/python_server_config.json"))
35
+ # dotnet_sdk_wrapper = _create_sdk_wrapper(Path("./aas_http_client/demo/dotnet_server_config.json"))
36
+
37
+ for existing_shell in wrapper.get_all_asset_administration_shells():
38
+ logger.warning(f"Delete shell '{existing_shell.id}'")
39
+ wrapper.delete_asset_administration_shell_by_id(existing_shell.id)
40
+
41
+ for existing_submodel in wrapper.get_all_submodels():
42
+ logger.warning(f"Delete submodel '{existing_submodel.id}'")
43
+ wrapper.delete_submodel_by_id(existing_submodel.id)
44
+
45
+ wrapper.post_asset_administration_shell(aas)
46
+ wrapper.post_submodel(submodel)
47
+
48
+ tmp = wrapper.get_asset_administration_shell_by_id_reference_aas_repository(aas.id)
49
+
50
+ shell = wrapper.get_asset_administration_shell_by_id(aas.id)
51
+ submodel = wrapper.get_submodel_by_id(submodel.id)
52
+
53
+ wrapper.post_submodel_element_submodel_repo(submodel.id, sme)
54
+
55
+ submodel = wrapper.get_submodel_by_id(submodel.id)
56
+
57
+ for existing_shell in wrapper.get_all_asset_administration_shells():
58
+ logger.warning(f"Delete shell '{existing_shell.id}'")
59
+ wrapper.delete_asset_administration_shell_by_id(existing_shell.id)
60
+
61
+ for existing_submodel in wrapper.get_all_submodels():
62
+ logger.warning(f"Delete submodel '{existing_submodel.id}'")
63
+ wrapper.delete_submodel_by_id(existing_submodel.id)
64
+
65
+
66
+ def _create_client(config: Path) -> AasHttpClient:
67
+ """Create a HTTP client from a given configuration file.
68
+
69
+ :param config: Given configuration file
70
+ :return: HTTP client
71
+ """
72
+ try:
73
+ file = config
74
+ client = create_client_by_config(file, password="")
75
+
76
+ except Exception as e:
77
+ logger.error(f"Failed to create client for {file}: {e}")
78
+
79
+ return client
80
+
81
+
82
+ def _create_sdk_wrapper(config: Path) -> SdkWrapper:
83
+ """Create a SDK wrapper from a given configuration file.
84
+
85
+ :param config: Given configuration file
86
+ :return: SDK wrapper
87
+ """
88
+ try:
89
+ file = config
90
+ client = create_wrapper_by_config(file, password="")
91
+
92
+ except Exception as e:
93
+ logger.error(f"Failed to create client for {file}: {e}")
94
+
95
+ return client
@@ -4,9 +4,10 @@ Provides some helper methods for easier work with basyx sdk data model
4
4
  """
5
5
 
6
6
  import uuid
7
+ from typing import Any
7
8
 
8
9
  from basyx.aas import model
9
- from typing import Any
10
+
10
11
 
11
12
  def create_unique_short_id(id_short: str) -> str:
12
13
  """Generate a unique identifier string by appending a UUID to the provided ID short.
@@ -16,41 +17,39 @@ def create_unique_short_id(id_short: str) -> str:
16
17
  """
17
18
  return f"{id_short}_{str(uuid.uuid4()).replace('-', '_')}"
18
19
 
19
- def create_base_submodel_element_Property(id_short: str, type: model.datatypes, value: Any, display_name: str = "", description: str = "") -> model.Property:
20
- """Create a basic Property Submodel Element.
21
- """
22
- sme = model.Property(
23
- id_short=id_short,
24
- value_type=type,
25
- value=value)
26
-
20
+
21
+ def create_base_submodel_element_Property(
22
+ id_short: str, type: model.datatypes, value: Any, display_name: str = "", description: str = ""
23
+ ) -> model.Property:
24
+ """Create a basic Property Submodel Element."""
25
+ sme = model.Property(id_short=id_short, value_type=type, value=value)
26
+
27
27
  if not description:
28
28
  description = f"This is the submodel element with ID short '{id_short}'"
29
29
 
30
30
  description_text = {"en": f"{description}"}
31
31
  sme.description = model.MultiLanguageTextType(description_text)
32
-
32
+
33
33
  if not display_name:
34
34
  display_name = "POC Submodel Element"
35
35
 
36
36
  display_name_text = {"en": f"{display_name}"}
37
37
  sme.display_name = model.MultiLanguageTextType(display_name_text)
38
-
38
+
39
39
  return sme
40
40
 
41
- def create_base_submodel(id_short: str, namespace: str = "basyx_python_aas_server", display_name: str = "", description: str = "") -> model.Submodel:
41
+
42
+ def create_base_submodel(id_short: str, namespace: str = "fluid40", display_name: str = "", description: str = "") -> model.Submodel:
42
43
  """Create a basic Submodel.
43
44
 
44
45
  :param id_short: ID short of the Submodel
45
- :param namespace: namespace of the Submodel , defaults to "basyx_python_aas_server"
46
+ :param namespace: namespace of the Submodel , defaults to "fluid40"
46
47
  :param display_name: display name of the Submodel, defaults to ""
47
48
  :param description: description of the Submodel, defaults to ""
48
49
  :return: Submodel instance
49
50
  """
50
- if namespace:
51
- identifier = f"{namespace}/{id_short}"
52
- else:
53
- identifier = id_short
51
+ identifier = f"{namespace}/{id_short}" if namespace else id_short
52
+
54
53
  sm = model.Submodel(identifier)
55
54
  sm.id_short = id_short
56
55
 
@@ -69,9 +68,7 @@ def create_base_submodel(id_short: str, namespace: str = "basyx_python_aas_serve
69
68
  return sm
70
69
 
71
70
 
72
- def create_base_ass(
73
- id_short: str, namespace: str = "basyx_python_aas_server", display_name: str = "", description: str = ""
74
- ) -> model.AssetAdministrationShell:
71
+ def create_base_ass(id_short: str, namespace: str = "fluid40", display_name: str = "", description: str = "") -> model.AssetAdministrationShell:
75
72
  """Create a basic AAS.
76
73
 
77
74
  :param id_short: ID short of the AAS
@@ -117,10 +114,7 @@ def create_base_asset_information(id_short: str, namespace: str = "basyx_python_
117
114
  :param namespace: namespace of the AssetInformation, defaults to "basyx_python_aas_server"
118
115
  :return: AssetInformation instance
119
116
  """
120
- if namespace:
121
- identifier = f"{namespace}/{id_short}"
122
- else:
123
- identifier = id_short
117
+ identifier = f"{namespace}/{id_short}" if namespace else id_short
124
118
  return model.AssetInformation(model.AssetKind.INSTANCE, identifier)
125
119
 
126
120
 
@@ -6,17 +6,20 @@ from pathlib import Path
6
6
  from typing import Any
7
7
 
8
8
  import basyx.aas.adapter.json
9
-
10
9
  from basyx.aas import model
10
+
11
11
  from aas_http_client.client import AasHttpClient, _create_client
12
+
12
13
  logger = logging.getLogger(__name__)
13
14
 
14
- class SdkWrapper():
15
+
16
+ class SdkWrapper:
15
17
  """Represents a wrapper for the BaSyx Python SDK to communicate with a REST API."""
16
- _client: AasHttpClient = None
17
- base_url: str = ""
18
18
 
19
- # region shells
19
+ _client: AasHttpClient = None
20
+ base_url: str = ""
21
+
22
+ # region shells
20
23
 
21
24
  def post_asset_administration_shell(self, aas: model.AssetAdministrationShell) -> model.AssetAdministrationShell | None:
22
25
  """Creates a new Asset Administration Shell.
@@ -54,7 +57,7 @@ class SdkWrapper():
54
57
  :return: Asset Administration Shells objects or None if an error occurred
55
58
  """
56
59
  content: dict = self._client.get_all_asset_administration_shells()
57
-
60
+
58
61
  if not content:
59
62
  return None
60
63
 
@@ -79,16 +82,16 @@ class SdkWrapper():
79
82
 
80
83
  def get_asset_administration_shell_by_id(self, aas_id: str) -> model.AssetAdministrationShell | None:
81
84
  """Returns a specific Asset Administration Shell.
82
-
85
+
83
86
  :param aas_id: ID of the AAS to retrieve
84
87
  :return: Asset Administration Shells object or None if an error occurred
85
88
  """
86
89
  content: dict = self._client.get_asset_administration_shell_by_id(aas_id)
87
-
90
+
88
91
  if not content:
89
92
  logger.warning(f"No shell found with ID '{aas_id}' on server.")
90
93
  return None
91
-
94
+
92
95
  return _to_object(content)
93
96
 
94
97
  def get_asset_administration_shell_by_id_reference_aas_repository(self, aas_id: str) -> model.Reference | None:
@@ -97,12 +100,12 @@ class SdkWrapper():
97
100
  :param aas_id: ID of the AAS reference to retrieve
98
101
  :return: Asset Administration Shells reference object or None if an error occurred
99
102
  """
100
- #workaround because serialization not working
103
+ # workaround because serialization not working
101
104
  aas = self.get_asset_administration_shell_by_id(aas_id)
102
105
  return model.ModelReference.from_referable(aas)
103
-
104
- # content: dict = self._client.get_shells_reference_by_id(aas_id)
105
- # return json.loads(content, cls=basyx.aas.adapter.json.AASFromJsonDecoder)
106
+
107
+ # content: dict = self._client.get_asset_administration_shell_by_id_reference_aas_repository(aas_id)
108
+ # return _to_object(content)
106
109
 
107
110
  def get_submodel_by_id_aas_repository(self, aas_id: str, submodel_id: str) -> model.Submodel | None:
108
111
  """Returns the Submodel.
@@ -122,9 +125,9 @@ class SdkWrapper():
122
125
  """
123
126
  return self._client.delete_asset_administration_shell_by_id(aas_id)
124
127
 
125
- # endregion
128
+ # endregion
126
129
 
127
- # region submodels
130
+ # region submodels
128
131
 
129
132
  def post_submodel(self, submodel: model.Submodel) -> model.Submodel | None:
130
133
  """Creates a new Submodel.
@@ -186,15 +189,15 @@ class SdkWrapper():
186
189
  if not content:
187
190
  logger.warning(f"No submodel found with ID '{submodel_id}' on server.")
188
191
  return None
189
-
192
+
190
193
  return _to_object(content)
191
194
 
192
195
  def patch_submodel_by_id(self, submodel_id: str, submodel: model.Submodel):
193
- """Updates an existing Submodel
196
+ """Updates an existing Submodel.
194
197
 
195
198
  :param submodel_id: Encoded ID of the Submodel to delete
196
199
  :return: True if the patch was successful, False otherwise
197
- """
200
+ """
198
201
  sm_data = _to_dict(submodel)
199
202
  return self._client.patch_submodel_by_id(submodel_id, sm_data)
200
203
 
@@ -206,15 +209,17 @@ class SdkWrapper():
206
209
  """
207
210
  return self._client.delete_submodel_by_id(submodel_id)
208
211
 
209
- def get_all_submodel_elements_submodel_repository(self, submodel_id: str, ) -> list[model.SubmodelElement] | None:
210
- """Returns all submodel elements including their hierarchy.
211
- !!! Serialization to model.SubmodelElement currently not possible
212
+ def get_all_submodel_elements_submodel_repository(
213
+ self,
214
+ submodel_id: str,
215
+ ) -> list[model.SubmodelElement] | None:
216
+ """Returns all submodel elements including their hierarchy. !!!Serialization to model.SubmodelElement currently not possible.
212
217
 
213
218
  :param submodel_id: Encoded ID of the Submodel to retrieve elements from
214
219
  :return: List of Submodel elements or None if an error occurred
215
220
  """
216
221
  content = self._client.get_all_submodel_elements_submodel_repository(submodel_id)
217
-
222
+
218
223
  if not content:
219
224
  return []
220
225
 
@@ -238,9 +243,8 @@ class SdkWrapper():
238
243
  return submodel_elements
239
244
 
240
245
  def post_submodel_element_submodel_repo(self, submodel_id: str, submodel_element: model.SubmodelElement) -> model.SubmodelElement | None:
241
- """Creates a new submodel element.
242
- !!! Serialization to model.SubmodelElements currently not possible
243
-
246
+ """Creates a new submodel element. !!!Serialization to model.SubmodelElements currently not possible.
247
+
244
248
  :param submodel_id: Encoded ID of the submodel to create elements for
245
249
  :param submodel_element: Submodel element to create
246
250
  :return: List of submodel element objects or None if an error occurred
@@ -252,6 +256,7 @@ class SdkWrapper():
252
256
 
253
257
  # endregion
254
258
 
259
+
255
260
  def _to_object(content: dict) -> Any | None:
256
261
  try:
257
262
  dict_string = json.dumps(content)
@@ -260,7 +265,8 @@ def _to_object(content: dict) -> Any | None:
260
265
  logger.error(f"Decoding error: {e}")
261
266
  logger.error(f"In JSON: {content}")
262
267
  return None
263
-
268
+
269
+
264
270
  def _to_dict(object: Any) -> dict | None:
265
271
  try:
266
272
  data_string = json.dumps(object, cls=basyx.aas.adapter.json.AASToJsonEncoder)
@@ -268,10 +274,12 @@ def _to_dict(object: Any) -> dict | None:
268
274
  except Exception as e:
269
275
  logger.error(f"Encoding error: {e}")
270
276
  logger.error(f"In object: {object}")
271
- return None
277
+ return None
278
+
272
279
 
273
280
  # region wrapper
274
281
 
282
+
275
283
  def create_wrapper_by_url(
276
284
  base_url: str,
277
285
  username: str = "",
@@ -304,17 +312,18 @@ def create_wrapper_by_url(
304
312
  config_dict["connection_time_out"] = connection_time_out
305
313
  config_dict["ssl_verify"] = ssl_verify
306
314
  config_string = json.dumps(config_dict, indent=4)
307
-
315
+
308
316
  wrapper = SdkWrapper()
309
- client = _create_client(config_string, password)
310
-
317
+ client = _create_client(config_string, password)
318
+
311
319
  if not client:
312
320
  return None
313
-
321
+
314
322
  wrapper._client = client
315
- wrapper.base_url = client.base_url
323
+ wrapper.base_url = client.base_url
316
324
  return wrapper
317
-
325
+
326
+
318
327
  def create_wrapper_by_config(config_file: Path, password: str = "") -> SdkWrapper | None:
319
328
  """Create a wrapper for the BaSyx Python SDK from the given parameters.
320
329
 
@@ -332,12 +341,13 @@ def create_wrapper_by_config(config_file: Path, password: str = "") -> SdkWrappe
332
341
 
333
342
  wrapper = SdkWrapper()
334
343
  client = _create_client(config_string, password)
335
-
344
+
336
345
  if not client:
337
346
  return None
338
-
339
- wrapper._client = client
340
- wrapper.base_url = client.base_url
347
+
348
+ wrapper._client = client
349
+ wrapper.base_url = client.base_url
341
350
  return wrapper
342
351
 
343
- # endregion
352
+
353
+ # endregion
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aas-http-client
3
- Version: 0.2.5
3
+ Version: 0.2.61
4
4
  Summary: Generic python HTTP client for communication with various types of AAS servers
5
5
  Author-email: Daniel Klein <daniel.klein@em.ag>
6
6
  License: # :em engineering methods AG Software License
@@ -13,4 +13,6 @@ aas_http_client/demo/demo_process.py
13
13
  aas_http_client/demo/logging_handler.py
14
14
  aas_http_client/utilities/__init__.py
15
15
  aas_http_client/utilities/model_builder.py
16
- aas_http_client/wrapper/sdk_wrapper.py
16
+ aas_http_client/wrapper/sdk_wrapper.py
17
+ tests/test_client.py
18
+ tests/test_wrapper.py
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "aas-http-client"
7
+ version = "0.2.61"
8
+ description = "Generic python HTTP client for communication with various types of AAS servers"
9
+ readme = "README.md"
10
+ license = { file = "LICENSE" }
11
+ authors = [{ name = "Daniel Klein", email = "daniel.klein@em.ag" }]
12
+ dependencies = []
13
+
14
+ [project.urls]
15
+ Homepage = "https://github.com/fluid40/aas-http-client"
16
+
17
+ [tool.commitizen]
18
+ name = "cz_conventional_commits"
19
+ version = "0.1.0"
20
+
21
+ [tool.commitizen.branch]
22
+ allowed = "^(main|master|develop|feature/.*|fix/.*|hotfix/.*)$"
@@ -0,0 +1,326 @@
1
+ import pytest
2
+ from pathlib import Path
3
+ from aas_http_client.client import create_client_by_config, AasHttpClient
4
+ from basyx.aas import model
5
+ import aas_http_client.utilities.model_builder as model_builder
6
+ import json
7
+ import basyx.aas.adapter.json
8
+
9
+ JAVA_SERVER_PORT = "8075"
10
+
11
+ CONFIG_FILES = [
12
+ "./tests/server_configs/test_dotnet_server_config.json",
13
+ "./tests/server_configs/test_java_server_config.json",
14
+ ]
15
+
16
+ @pytest.fixture(params=CONFIG_FILES, scope="module")
17
+ def client(request) -> AasHttpClient:
18
+ try:
19
+ file = Path(request.param).resolve()
20
+
21
+ if not file.exists():
22
+ raise FileNotFoundError(f"Configuration file {file} does not exist.")
23
+
24
+ client = create_client_by_config(file, password="")
25
+ except Exception as e:
26
+ raise RuntimeError("Unable to connect to server.")
27
+
28
+ shells = client.get_all_asset_administration_shells()
29
+ if shells is None:
30
+ raise RuntimeError("No shells found on server. Please check the server configuration.")
31
+
32
+ return client
33
+
34
+ @pytest.fixture(scope="module")
35
+ def shared_sme() -> model.Property:
36
+ # create a Submodel
37
+ return model_builder.create_base_submodel_element_Property("sme_http_client_unit_tests", model.datatypes.String, "Sample Value")
38
+
39
+ @pytest.fixture(scope="module")
40
+ def shared_sm() -> model.Submodel:
41
+ # create a Submodel
42
+ return model_builder.create_base_submodel("sm_http_client_unit_tests", "")
43
+
44
+ @pytest.fixture(scope="module")
45
+ def shared_aas(shared_sm: model.Submodel) -> model.AssetAdministrationShell:
46
+ # create an AAS
47
+ aas = model_builder.create_base_ass(id_short="aas_http_client_unit_tests", namespace="")
48
+
49
+ # add Submodel to AAS
50
+ model_builder.add_submodel_to_aas(aas, shared_sm)
51
+
52
+ return aas
53
+
54
+ def test_001_connect(client: AasHttpClient):
55
+ assert client is not None
56
+
57
+ def test_002_get_all_asset_administration_shells(client: AasHttpClient):
58
+ result = client.get_all_asset_administration_shells()
59
+ assert result is not None
60
+ shells = result.get("result", [])
61
+ assert len(shells) == 0
62
+
63
+ def test_003_post_asset_administration_shell(client: AasHttpClient, shared_aas: model.AssetAdministrationShell):
64
+ aas_data_string = json.dumps(shared_aas, cls=basyx.aas.adapter.json.AASToJsonEncoder)
65
+ aas_data = json.loads(aas_data_string)
66
+ result = client.post_asset_administration_shell(aas_data)
67
+
68
+ assert result is not None
69
+ result_id_short = result.get("idShort", "")
70
+ assert result_id_short == shared_aas.id_short
71
+
72
+ get_result = client.get_all_asset_administration_shells()
73
+ assert get_result is not None
74
+ shells = get_result.get("result", [])
75
+ assert len(shells) == 1
76
+ assert shells[0].get("idShort", "") == shared_aas.id_short
77
+
78
+ def test_004a_get_asset_administration_shell_by_id(client: AasHttpClient, shared_aas: model.AssetAdministrationShell):
79
+ result = client.get_asset_administration_shell_by_id(shared_aas.id)
80
+
81
+ assert result is not None
82
+ assert result.get("idShort", "") == shared_aas.id_short
83
+
84
+ def test_004b_get_asset_administration_shell_by_id(client: AasHttpClient):
85
+ result = client.get_asset_administration_shell_by_id("non_existent_id")
86
+
87
+ assert result is None
88
+
89
+ def test_005a_put_asset_administration_shell_by_id(client: AasHttpClient, shared_aas: model.AssetAdministrationShell):
90
+ aas = model.AssetAdministrationShell(id_=shared_aas.asset_information.global_asset_id, asset_information=shared_aas.asset_information)
91
+ aas.id_short = shared_aas.id_short
92
+
93
+ description_text = "Put description for unit tests"
94
+ aas.description = model.MultiLanguageTextType({"en": description_text})
95
+ aas.submodel = shared_aas.submodel # Keep existing submodels
96
+
97
+ aas_data_string = json.dumps(aas, cls=basyx.aas.adapter.json.AASToJsonEncoder)
98
+ aas_data = json.loads(aas_data_string)
99
+
100
+ result = client.put_asset_administration_shell_by_id(shared_aas.id, aas_data)
101
+
102
+ assert result
103
+
104
+ get_result = client.get_asset_administration_shell_by_id(shared_aas.id)
105
+
106
+ assert get_result
107
+ assert get_result.get("idShort", "") == shared_aas.id_short
108
+ assert get_result.get("id", "") == shared_aas.id
109
+ # description must have changed
110
+ assert get_result.get("description", {})[0].get("text", "") == description_text
111
+ assert get_result.get("description", {})[0].get("text", "") != shared_aas.description.get("en", "")
112
+ # submodels must be retained
113
+ assert len(get_result.get("submodels", [])) == len(shared_aas.submodel)
114
+
115
+ # The display name must be empty
116
+ # currently not working in dotnet
117
+ # assert len(get_result.get("displayName", {})) == 0
118
+
119
+ def test_005b_put_asset_administration_shell_by_id(client: AasHttpClient, shared_aas: model.AssetAdministrationShell):
120
+ # put with other ID
121
+ id_short = "put_short_id"
122
+ asset_info = model_builder.create_base_asset_information(id_short)
123
+ aas = model.AssetAdministrationShell(id_=asset_info.global_asset_id, asset_information=asset_info)
124
+ aas.id_short = id_short
125
+
126
+ description_text = {"en": "Updated description for unit tests"}
127
+ aas.description = model.MultiLanguageTextType(description_text)
128
+
129
+ aas_data_string = json.dumps(aas, cls=basyx.aas.adapter.json.AASToJsonEncoder)
130
+ aas_data = json.loads(aas_data_string)
131
+
132
+ result = client.put_asset_administration_shell_by_id(shared_aas.id, aas_data)
133
+
134
+ assert not result
135
+
136
+ def test_006_get_asset_administration_shell_by_id_reference_aas_repository(client: AasHttpClient, shared_aas: model.AssetAdministrationShell):
137
+ result = client.get_asset_administration_shell_by_id_reference_aas_repository(shared_aas.id)
138
+
139
+ if JAVA_SERVER_PORT in client.base_url:
140
+ # Basyx java server do not provide this endpoint
141
+ assert result is None
142
+ else:
143
+ assert result is not None
144
+ keys = result.get("keys", [])
145
+ assert len(keys) == 1
146
+ assert keys[0].get("value", "") == shared_aas.id
147
+
148
+ def test_007_get_submodel_by_id_aas_repository(client: AasHttpClient, shared_aas: model.AssetAdministrationShell, shared_sm: model.Submodel):
149
+ result = client.get_submodel_by_id_aas_repository(shared_aas.id, shared_sm.id)
150
+
151
+ assert result is None
152
+
153
+ def test_008_get_all_submodels(client: AasHttpClient):
154
+ result = client.get_all_submodels()
155
+ assert result is not None
156
+ submodels = result.get("result", [])
157
+ assert len(submodels) == 0
158
+
159
+ def test_009_post_submodel(client: AasHttpClient, shared_sm: model.Submodel):
160
+ sm_data_string = json.dumps(shared_sm, cls=basyx.aas.adapter.json.AASToJsonEncoder)
161
+ sm_data = json.loads(sm_data_string)
162
+
163
+ result = client.post_submodel(sm_data)
164
+
165
+ assert result is not None
166
+ result_id_short = result.get("idShort", "")
167
+ assert result_id_short == shared_sm.id_short
168
+
169
+ get_result = client.get_all_submodels()
170
+ assert get_result is not None
171
+ submodels = get_result.get("result", [])
172
+ assert len(submodels) == 1
173
+ assert submodels[0].get("idShort", "") == shared_sm.id_short
174
+
175
+ def test_010_get_submodel_by_id_aas_repository(client: AasHttpClient, shared_aas: model.AssetAdministrationShell, shared_sm: model.Submodel):
176
+ result = client.get_submodel_by_id_aas_repository(shared_aas.id, shared_sm.id)
177
+
178
+ if JAVA_SERVER_PORT in client.base_url:
179
+ # Basyx java server do not provide this endpoint
180
+ assert result is None
181
+ else:
182
+ assert result is not None
183
+ result_id_short = result.get("idShort", "")
184
+ assert result_id_short == shared_sm.id_short
185
+
186
+ def test_011a_get_submodel_by_id(client: AasHttpClient, shared_sm: model.Submodel):
187
+ result = client.get_submodel_by_id(shared_sm.id)
188
+
189
+ assert result is not None
190
+ result_id_short = result.get("idShort", "")
191
+ assert result_id_short == shared_sm.id_short
192
+
193
+ def test_011b_get_submodel_by_id(client: AasHttpClient):
194
+ result = client.get_submodel_by_id("non_existent_id")
195
+
196
+ assert result is None
197
+
198
+ def test_012_patch_submodel_by_id(client: AasHttpClient, shared_sm: model.Submodel):
199
+ sm = model.Submodel(shared_sm.id_short)
200
+ sm.id_short = shared_sm.id_short
201
+
202
+ description_text = "Patched description for unit tests"
203
+ sm.description = model.MultiLanguageTextType({"en": description_text})
204
+
205
+ sm_data_string = json.dumps(sm, cls=basyx.aas.adapter.json.AASToJsonEncoder)
206
+ sm_data = json.loads(sm_data_string)
207
+
208
+ result = client.patch_submodel_by_id(shared_sm.id, sm_data)
209
+
210
+ if JAVA_SERVER_PORT in client.base_url:
211
+ # Basyx java server do not provide this endpoint
212
+ assert not result
213
+ else:
214
+ assert result
215
+
216
+ get_result = client.get_submodel_by_id(shared_sm.id)
217
+ assert get_result is not None
218
+ assert get_result.get("idShort", "") == shared_sm.id_short
219
+ assert get_result.get("id", "") == shared_sm.id
220
+ # Only the description may change in patch.
221
+ assert get_result.get("description", {})[0].get("text", "") == description_text
222
+ assert get_result.get("description", {})[0].get("text", "") != shared_sm.description.get("en", "")
223
+ # The display name must remain the same.
224
+ assert get_result.get("displayName", {})[0].get("text", "") == shared_sm.display_name.get("en", "")
225
+
226
+ def test_013_put_submodel_by_id_aas_repository(client: AasHttpClient, shared_aas: model.AssetAdministrationShell, shared_sm: model.Submodel):
227
+ sm = model.Submodel(shared_sm.id_short)
228
+ sm.id_short = shared_sm.id_short
229
+
230
+ description_text = "Put via shell description for unit tests"
231
+ sm.description = model.MultiLanguageTextType({"en": description_text})
232
+
233
+ sm_data_string = json.dumps(sm, cls=basyx.aas.adapter.json.AASToJsonEncoder)
234
+ sm_data = json.loads(sm_data_string)
235
+
236
+ result = client.put_submodel_by_id_aas_repository(shared_aas.id, shared_sm.id, sm_data)
237
+
238
+ if JAVA_SERVER_PORT in client.base_url:
239
+ # Basyx java server do not provide this endpoint
240
+ assert not result
241
+ else:
242
+ assert result
243
+
244
+ get_result = client.get_submodel_by_id_aas_repository(shared_aas.id, shared_sm.id)
245
+ assert get_result is not None
246
+ assert get_result.get("idShort", "") == shared_sm.id_short
247
+ assert get_result.get("id", "") == shared_sm.id
248
+ # description must have changed
249
+ assert get_result.get("description", {})[0].get("text", "") == description_text
250
+ assert get_result.get("description", {})[0].get("text", "") != shared_sm.description.get("en", "")
251
+ assert len(get_result.get("displayName", {})) == 0
252
+
253
+ # restore to its original state
254
+ sm_data_string = json.dumps(shared_sm, cls=basyx.aas.adapter.json.AASToJsonEncoder)
255
+ sm_data = json.loads(sm_data_string)
256
+ client.put_submodel_by_id_aas_repository(shared_aas.id, shared_sm.id, sm_data) # Restore original submodel
257
+
258
+ def test_014_put_submodels_by_id(client: AasHttpClient, shared_sm: model.Submodel):
259
+ sm = model.Submodel(shared_sm.id_short)
260
+ sm.id_short = shared_sm.id_short
261
+
262
+ description_text = "Put description for unit tests"
263
+ sm.description = model.MultiLanguageTextType({"en": description_text})
264
+
265
+ sm_data_string = json.dumps(sm, cls=basyx.aas.adapter.json.AASToJsonEncoder)
266
+ sm_data = json.loads(sm_data_string)
267
+
268
+ result = client.put_submodels_by_id(shared_sm.id, sm_data)
269
+
270
+ assert result
271
+
272
+ get_result = client.get_submodel_by_id(shared_sm.id)
273
+ assert get_result is not None
274
+ assert get_result.get("idShort", "") == shared_sm.id_short
275
+ assert get_result.get("id", "") == shared_sm.id
276
+ # description must have changed
277
+ assert get_result.get("description", {})[0].get("text", "") == description_text
278
+ assert get_result.get("description", {})[0].get("text", "") != shared_sm.description.get("en", "")
279
+ assert len(get_result.get("displayName", {})) == 0
280
+
281
+ # restore to its original state
282
+ sm_data_string = json.dumps(shared_sm, cls=basyx.aas.adapter.json.AASToJsonEncoder)
283
+ sm_data = json.loads(sm_data_string)
284
+ client.put_submodels_by_id(shared_sm.id, sm_data) # Restore original submodel
285
+
286
+ def test_015_get_all_submodel_elements_submodel_repository(client: AasHttpClient, shared_sm: model.Submodel):
287
+ submodel_elements = client.get_all_submodel_elements_submodel_repository(shared_sm.id)
288
+
289
+ assert submodel_elements is not None
290
+ assert len(submodel_elements.get("result", [])) == 0
291
+
292
+ def test_016_post_submodel_element_submodel_repo(client: AasHttpClient, shared_sm: model.Submodel, shared_sme: model.Property):
293
+ sme_data_string = json.dumps(shared_sme, cls=basyx.aas.adapter.json.AASToJsonEncoder)
294
+ sme_data = json.loads(sme_data_string)
295
+
296
+ result = client.post_submodel_element_submodel_repo(shared_sm.id, sme_data)
297
+
298
+ assert result is not None
299
+ assert result.get("idShort", "") == shared_sme.id_short
300
+ assert result.get("description", {})[0].get("text", "") == shared_sme.description.get("en", "")
301
+ assert result.get("displayName", {})[0].get("text", "") == shared_sme.display_name.get("en", "")
302
+
303
+ get_result = client.get_all_submodel_elements_submodel_repository(shared_sm.id)
304
+
305
+ assert get_result is not None
306
+ assert len(get_result.get("result", [])) == 1
307
+
308
+ def test_098_delete_asset_administration_shell_by_id(client: AasHttpClient, shared_aas: model.AssetAdministrationShell):
309
+ result = client.delete_asset_administration_shell_by_id(shared_aas.id)
310
+
311
+ assert result
312
+
313
+ get_result = client.get_all_asset_administration_shells()
314
+ assert get_result is not None
315
+ shells = get_result.get("result", [])
316
+ assert len(shells) == 0
317
+
318
+ def test_099_delete_submodel_by_id(client: AasHttpClient, shared_sm: model.Submodel):
319
+ result = client.delete_submodel_by_id(shared_sm.id)
320
+
321
+ assert result
322
+
323
+ get_result = client.get_all_submodels()
324
+ assert get_result is not None
325
+ submodels = get_result.get("result", [])
326
+ assert len(submodels) == 0
@@ -0,0 +1,308 @@
1
+ import pytest
2
+ from pathlib import Path
3
+ from aas_http_client.wrapper.sdk_wrapper import create_wrapper_by_config, SdkWrapper
4
+ from basyx.aas import model
5
+ import aas_http_client.utilities.model_builder as model_builder
6
+
7
+ JAVA_SERVER_PORT = "8075"
8
+
9
+ CONFIG_FILES = [
10
+ "./tests/server_configs/test_dotnet_server_config.json",
11
+ "./tests/server_configs/test_java_server_config.json"
12
+ ]
13
+
14
+ @pytest.fixture(params=CONFIG_FILES, scope="module")
15
+ def wrapper(request) -> SdkWrapper:
16
+ try:
17
+ file = Path(request.param).resolve()
18
+
19
+ if not file.exists():
20
+ raise FileNotFoundError(f"Configuration file {file} does not exist.")
21
+
22
+ wrapper = create_wrapper_by_config(file, password="")
23
+ except Exception as e:
24
+ raise RuntimeError("Unable to connect to server.")
25
+
26
+ shells = wrapper.get_all_asset_administration_shells()
27
+ if shells is None:
28
+ raise RuntimeError("No shells found on server. Please check the server configuration.")
29
+
30
+ return wrapper
31
+
32
+ @pytest.fixture(scope="module")
33
+ def shared_sme() -> model.Property:
34
+ # create a Submodel
35
+ return model_builder.create_base_submodel_element_Property("sme_http_client_unit_tests", model.datatypes.String, "Sample Value")
36
+
37
+ @pytest.fixture(scope="module")
38
+ def shared_sm() -> model.Submodel:
39
+ # create a Submodel
40
+ submodel = model_builder.create_base_submodel("sm_http_client_unit_tests", "")
41
+ submodel.category = "Unit Test"
42
+ return submodel
43
+
44
+ @pytest.fixture(scope="module")
45
+ def shared_aas(shared_sm: model.Submodel) -> model.AssetAdministrationShell:
46
+ # create an AAS
47
+ aas = model_builder.create_base_ass(id_short="aas_http_client_unit_tests", namespace="")
48
+
49
+ # add Submodel to AAS
50
+ model_builder.add_submodel_to_aas(aas, shared_sm)
51
+
52
+ return aas
53
+
54
+ def test_001_connect(wrapper: SdkWrapper):
55
+ assert wrapper is not None
56
+
57
+ def test_002_get_all_asset_administration_shells(wrapper: SdkWrapper):
58
+ shells = wrapper.get_all_asset_administration_shells()
59
+ assert shells is not None
60
+ assert len(shells) == 0
61
+
62
+ def test_003_post_asset_administration_shell(wrapper: SdkWrapper, shared_aas: model.AssetAdministrationShell):
63
+ shell = wrapper.post_asset_administration_shell(shared_aas)
64
+
65
+ assert shell is not None
66
+ assert shell.id == shared_aas.id
67
+ assert shell.id_short == shared_aas.id_short
68
+
69
+ shells = wrapper.get_all_asset_administration_shells()
70
+ assert shells is not None
71
+ assert len(shells) == 1
72
+ assert shells[0].id_short == shared_aas.id_short
73
+ assert shells[0].id == shared_aas.id
74
+
75
+ def test_004a_get_asset_administration_shell_by_id(wrapper: SdkWrapper, shared_aas: model.AssetAdministrationShell):
76
+ shell = wrapper.get_asset_administration_shell_by_id(shared_aas.id)
77
+
78
+ assert shell is not None
79
+ assert shell.id_short == shared_aas.id_short
80
+ assert shell.id == shared_aas.id
81
+
82
+ def test_004b_get_asset_administration_shell_by_id(wrapper: SdkWrapper):
83
+ shell = wrapper.get_asset_administration_shell_by_id("non_existent_id")
84
+
85
+ assert shell is None
86
+
87
+ def test_005a_put_asset_administration_shell_by_id(wrapper: SdkWrapper, shared_aas: model.AssetAdministrationShell):
88
+ aas = model.AssetAdministrationShell(id_=shared_aas.asset_information.global_asset_id, asset_information=shared_aas.asset_information)
89
+ aas.id_short = shared_aas.id_short
90
+
91
+ description_text = "Put description for unit tests"
92
+ aas.description = model.MultiLanguageTextType({"en": description_text})
93
+ aas.submodel = shared_aas.submodel # Keep existing submodels
94
+
95
+ result = wrapper.put_asset_administration_shell_by_id(shared_aas.id, aas)
96
+
97
+ assert result
98
+
99
+ shell = wrapper.get_asset_administration_shell_by_id(shared_aas.id)
100
+
101
+ assert shell is not None
102
+ assert shell.id_short == shared_aas.id_short
103
+ assert shell.id == shared_aas.id
104
+ # description must have changed
105
+ assert shell.description.get("en", "") == description_text
106
+ assert shell.description.get("en", "") != shared_aas.description.get("en", "")
107
+ # submodels must be retained
108
+ assert len(shell.submodel) == len(shared_aas.submodel)
109
+
110
+ # The display name must be empty
111
+ # currently not working in dotnet
112
+ # assert len(get_result.get("displayName", {})) == 0
113
+
114
+ def test_005b_put_asset_administration_shell_by_id(wrapper: SdkWrapper, shared_aas: model.AssetAdministrationShell):
115
+ # put with other ID
116
+ id_short = "put_short_id"
117
+ asset_info = model_builder.create_base_asset_information(id_short)
118
+ aas = model.AssetAdministrationShell(id_=asset_info.global_asset_id, asset_information=asset_info)
119
+ aas.id_short = id_short
120
+
121
+ description_text = {"en": "Updated description for unit tests"}
122
+ aas.description = model.MultiLanguageTextType(description_text)
123
+
124
+ result = wrapper.put_asset_administration_shell_by_id(shared_aas.id, aas)
125
+
126
+ assert not result
127
+
128
+ def test_006_get_asset_administration_shell_by_id_reference_aas_repository(wrapper: SdkWrapper, shared_aas: model.AssetAdministrationShell):
129
+ reference = wrapper.get_asset_administration_shell_by_id_reference_aas_repository(shared_aas.id)
130
+
131
+ assert reference is not None
132
+ assert len(reference.key) == 1
133
+ assert reference.key[0].value == shared_aas.id
134
+
135
+ def test_007_get_submodel_by_id_aas_repository(wrapper: SdkWrapper, shared_aas: model.AssetAdministrationShell, shared_sm: model.Submodel):
136
+ submodel = wrapper.get_submodel_by_id_aas_repository(shared_aas.id, shared_sm.id)
137
+
138
+ assert submodel is None
139
+
140
+ def test_008_get_all_submodels(wrapper: SdkWrapper):
141
+ submodels = wrapper.get_all_submodels()
142
+ assert submodels is not None
143
+ assert len(submodels) == 0
144
+
145
+ def test_009_post_submodel(wrapper: SdkWrapper, shared_sm: model.Submodel):
146
+ submodel = wrapper.post_submodel(shared_sm)
147
+
148
+ assert submodel is not None
149
+ assert submodel.id == shared_sm.id
150
+ assert submodel.id_short == shared_sm.id_short
151
+
152
+ submodels = wrapper.get_all_submodels()
153
+ assert submodels is not None
154
+ assert len(submodels) == 1
155
+ assert submodels[0].id_short == shared_sm.id_short
156
+ assert submodels[0].id == shared_sm.id
157
+
158
+ def test_010_get_submodel_by_id_aas_repository(wrapper: SdkWrapper, shared_aas: model.AssetAdministrationShell, shared_sm: model.Submodel):
159
+ submodel = wrapper.get_submodel_by_id_aas_repository(shared_aas.id, shared_sm.id)
160
+
161
+ if JAVA_SERVER_PORT in wrapper.base_url:
162
+ # Basyx java server do not provide this endpoint
163
+ assert submodel is None
164
+ else:
165
+ assert submodel is not None
166
+ assert submodel.id_short == shared_sm.id_short
167
+ assert submodel.id == shared_sm.id
168
+
169
+ def test_011a_get_submodel_by_id(wrapper: SdkWrapper, shared_sm: model.Submodel):
170
+ submodel = wrapper.get_submodel_by_id(shared_sm.id)
171
+
172
+ assert submodel is not None
173
+ assert submodel.id_short == shared_sm.id_short
174
+ assert submodel.id == shared_sm.id
175
+
176
+ def test_011b_get_submodel_by_id(wrapper: SdkWrapper):
177
+ result = wrapper.get_submodel_by_id("non_existent_id")
178
+
179
+ assert result is None
180
+
181
+ def test_012_patch_submodel_by_id(wrapper: SdkWrapper, shared_sm: model.Submodel):
182
+ sm = model.Submodel(shared_sm.id_short)
183
+ sm.id_short = shared_sm.id_short
184
+
185
+ description_text = "Patched description for unit tests"
186
+ sm.description = model.MultiLanguageTextType({"en": description_text})
187
+
188
+ result = wrapper.patch_submodel_by_id(shared_sm.id, sm)
189
+
190
+ if JAVA_SERVER_PORT in wrapper.base_url:
191
+ # Basyx java server do not provide this endpoint
192
+ assert not result
193
+ else:
194
+ assert result
195
+
196
+ submodel = wrapper.get_submodel_by_id(shared_sm.id)
197
+ assert submodel is not None
198
+ assert submodel.id_short == shared_sm.id_short
199
+ assert submodel.id == shared_sm.id
200
+ # Only the description may change in patch.
201
+ assert submodel.description.get("en", "") == description_text
202
+ assert submodel.description.get("en", "") != shared_sm.description.get("en", "")
203
+ # The display name must remain the same.
204
+ assert submodel.display_name == shared_sm.display_name
205
+ assert len(submodel.submodel_element) == len(shared_sm.submodel_element)
206
+
207
+ def test_013_put_submodel_by_id_aas_repository(wrapper: SdkWrapper, shared_aas: model.AssetAdministrationShell, shared_sm: model.Submodel):
208
+ sm = model.Submodel(shared_sm.id_short)
209
+ sm.id_short = shared_sm.id_short
210
+
211
+ description_text = "Put via shell description for unit tests"
212
+ sm.description = model.MultiLanguageTextType({"en": description_text})
213
+ sm.display_name = shared_sm.display_name # Keep existing display name because of problems with empty lists
214
+
215
+ result = wrapper.put_submodel_by_id_aas_repository(shared_aas.id, shared_sm.id, sm)
216
+
217
+ if JAVA_SERVER_PORT in wrapper.base_url:
218
+ # Basyx java server do not provide this endpoint
219
+ assert not result
220
+ else:
221
+ assert result
222
+
223
+ submodel = wrapper.get_submodel_by_id_aas_repository(shared_aas.id, shared_sm.id)
224
+ assert submodel is not None
225
+ assert submodel.id_short == shared_sm.id_short
226
+ assert submodel.id == shared_sm.id
227
+ # description must have changed
228
+ assert submodel.description.get("en", "") == description_text
229
+ assert submodel.description.get("en", "") != shared_sm.description.get("en", "")
230
+ # display name stays
231
+ assert submodel.display_name == shared_sm.display_name
232
+ # category was not set an must be empty
233
+ assert submodel.category is None
234
+ assert len(submodel.submodel_element) == 0
235
+
236
+ # restore to its original state
237
+ wrapper.put_submodel_by_id_aas_repository(shared_aas.id, shared_sm.id, shared_sm) # Restore original submodel
238
+
239
+ def test_014_put_submodels_by_id(wrapper: SdkWrapper, shared_sm: model.Submodel):
240
+ sm = model.Submodel(shared_sm.id_short)
241
+ sm.id_short = shared_sm.id_short
242
+
243
+ description_text = "Put description for unit tests"
244
+ sm.description = model.MultiLanguageTextType({"en": description_text})
245
+ sm.display_name = shared_sm.display_name # Keep existing display name because of problems with empty lists
246
+
247
+ result = wrapper.put_submodels_by_id(shared_sm.id, sm)
248
+
249
+ assert result
250
+
251
+ submodel = wrapper.get_submodel_by_id(shared_sm.id)
252
+ assert submodel is not None
253
+ assert submodel.id_short == shared_sm.id_short
254
+ assert submodel.id == shared_sm.id
255
+ # description must have changed
256
+ assert submodel.description.get("en", "") == description_text
257
+ assert submodel.description.get("en", "") != shared_sm.description.get("en", "")
258
+ # display name stays
259
+ # assert submodel.display_name == shared_sm.display_name
260
+ # category was not set an must be empty
261
+ assert submodel.category is None
262
+ assert len(submodel.submodel_element) == 0
263
+
264
+ # restore to its original state
265
+ wrapper.put_submodels_by_id(shared_sm.id, shared_sm) # Restore original submodel
266
+
267
+ def test_015_get_all_submodel_elements_submodel_repository(wrapper: SdkWrapper, shared_sm: model.Submodel):
268
+ submodel_elements = wrapper.get_all_submodel_elements_submodel_repository(shared_sm.id)
269
+
270
+ assert submodel_elements is not None
271
+ assert len(submodel_elements) == 0
272
+
273
+ def test_016_post_submodel_element_submodel_repo(wrapper: SdkWrapper, shared_sm: model.Submodel, shared_sme: model.Property):
274
+ submodel_element = wrapper.post_submodel_element_submodel_repo(shared_sm.id, shared_sme)
275
+
276
+ assert submodel_element is not None
277
+
278
+ # currently only dict is returned
279
+
280
+ # property: model.Property = submodel_element
281
+
282
+ # assert property.id_short == shared_sme.id_short
283
+ # assert property.description.get("em", "") == shared_sme.description.get("en", "")
284
+ # assert property.display_name.get("em", "") == shared_sme.display_name.get("en", "")
285
+ # assert property.value == shared_sme.value
286
+
287
+ submodel_elements = wrapper.get_all_submodel_elements_submodel_repository(shared_sm.id)
288
+
289
+ assert submodel_elements is not None
290
+ assert len(submodel_elements) == 1
291
+
292
+ def test_098_delete_asset_administration_shell_by_id(wrapper: SdkWrapper, shared_aas: model.AssetAdministrationShell):
293
+ result = wrapper.delete_asset_administration_shell_by_id(shared_aas.id)
294
+
295
+ assert result
296
+
297
+ shells = wrapper.get_all_asset_administration_shells()
298
+ assert shells is not None
299
+ assert len(shells) == 0
300
+
301
+ def test_099_delete_submodel_by_id(wrapper: SdkWrapper, shared_sm: model.Submodel):
302
+ result = wrapper.delete_submodel_by_id(shared_sm.id)
303
+
304
+ assert result
305
+
306
+ submodels = wrapper.get_all_submodels()
307
+ assert submodels is not None
308
+ assert len(submodels) == 0
@@ -1,93 +0,0 @@
1
- import logging
2
- import aas_http_client.utilities.model_builder as model_builder
3
- from aas_http_client.client import create_client_by_config, AasHttpClient
4
- from aas_http_client.wrapper.sdk_wrapper import SdkWrapper, create_wrapper_by_config
5
- from pathlib import Path
6
- import json
7
- import basyx.aas.adapter.json
8
- import basyx.aas.model
9
-
10
- from basyx.aas import model
11
-
12
- logger = logging.getLogger(__name__)
13
-
14
- def start():
15
- """Start the demo process."""
16
- # create a submodel element
17
- sme_short_id: str = model_builder.create_unique_short_id("poc_sme")
18
- sme = model_builder.create_base_submodel_element_Property(sme_short_id, model.datatypes.String, "Sample Value")
19
-
20
- # create a submodel
21
- sm_short_id: str = model_builder.create_unique_short_id("poc_sm")
22
- submodel = model_builder.create_base_submodel(sm_short_id)
23
- # add submodel element to submodel
24
- # submodel.submodel_element.add(sme)
25
-
26
- # create an AAS
27
- aas_short_id: str = model_builder.create_unique_short_id("poc_aas")
28
- aas = model_builder.create_base_ass(aas_short_id)
29
-
30
- # add submodel to AAS
31
- model_builder.add_submodel_to_aas(aas, submodel)
32
-
33
- java_sdk_wrapper = _create_sdk_wrapper(Path("./aas_http_client/demo/java_server_config.json"))
34
- # dotnet_sdk_wrapper = _create_sdk_wrapper(Path("./aas_http_client/demo/dotnet_server_config.json"))
35
-
36
- for existing_shell in java_sdk_wrapper.get_all_asset_administration_shells():
37
- logger.warning(f"Delete shell '{existing_shell.id}'")
38
- java_sdk_wrapper.delete_asset_administration_shell_by_id(existing_shell.id)
39
-
40
- for existing_submodel in java_sdk_wrapper.get_all_submodels():
41
- logger.warning(f"Delete submodel '{existing_submodel.id}'")
42
- java_sdk_wrapper.delete_submodel_by_id(existing_submodel.id)
43
-
44
- java_sdk_wrapper.post_asset_administration_shell(aas)
45
- java_sdk_wrapper.post_submodel(submodel)
46
-
47
- tmp = java_sdk_wrapper.get_asset_administration_shell_by_id_reference_aas_repository(aas.id)
48
-
49
- shell = java_sdk_wrapper.get_asset_administration_shell_by_id(aas.id)
50
- submodel = java_sdk_wrapper.get_submodel_by_id(submodel.id)
51
-
52
- java_sdk_wrapper.post_submodel_element_submodel_repo(submodel.id, sme)
53
-
54
- submodel = java_sdk_wrapper.get_submodel_by_id(submodel.id)
55
-
56
-
57
- def _create_shell() -> basyx.aas.model.AssetAdministrationShell:
58
- # create an AAS
59
- aas_short_id: str = model_builder.create_unique_short_id("poc_aas")
60
- aas = model_builder.create_base_ass(aas_short_id)
61
-
62
- # create a Submodel
63
- sm_short_id: str = model_builder.create_unique_short_id("poc_sm")
64
- submodel = model_builder.create_base_submodel(sm_short_id)
65
-
66
- # add Submodel to AAS
67
- model_builder.add_submodel_to_aas(aas, submodel)
68
-
69
- return aas
70
-
71
- def _create_client(config: Path) -> AasHttpClient:
72
- """Create client for java servers."""
73
-
74
- try:
75
- file = config
76
- client = create_client_by_config(file, password="")
77
- except Exception as e:
78
- logger.error(f"Failed to create client for {file}: {e}")
79
- pass
80
-
81
- return client
82
-
83
- def _create_sdk_wrapper(config: Path) -> SdkWrapper:
84
- """Create client for java servers."""
85
-
86
- try:
87
- file = config
88
- client = create_wrapper_by_config(file, password="")
89
- except Exception as e:
90
- logger.error(f"Failed to create client for {file}: {e}")
91
- pass
92
-
93
- return client
@@ -1,17 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools>=61.0"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "aas-http-client"
7
- version = "0.2.5"
8
- description = "Generic python HTTP client for communication with various types of AAS servers"
9
- readme = "README.md"
10
- license = { file = "LICENSE" }
11
- authors = [
12
- { name = "Daniel Klein", email = "daniel.klein@em.ag" }
13
- ]
14
- dependencies = []
15
-
16
- [project.urls]
17
- Homepage = "https://github.com/fluid40/aas-http-client"