aas-http-client 0.3.1__tar.gz → 0.3.3__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.
Files changed (21) hide show
  1. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/PKG-INFO +1 -1
  2. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/aas_http_client/client.py +55 -17
  3. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/aas_http_client/wrapper/sdk_wrapper.py +44 -22
  4. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/aas_http_client.egg-info/PKG-INFO +1 -1
  5. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/pyproject.toml +1 -1
  6. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/tests/test_client.py +205 -18
  7. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/tests/test_wrapper.py +232 -22
  8. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/LICENSE +0 -0
  9. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/README.md +0 -0
  10. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/aas_http_client/__init__.py +0 -0
  11. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/aas_http_client/core/encoder.py +0 -0
  12. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/aas_http_client/core/version_check.py +0 -0
  13. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/aas_http_client/demo/demo_process.py +0 -0
  14. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/aas_http_client/demo/logging_handler.py +0 -0
  15. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/aas_http_client/utilities/__init__.py +0 -0
  16. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/aas_http_client/utilities/model_builder.py +0 -0
  17. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/aas_http_client.egg-info/SOURCES.txt +0 -0
  18. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/aas_http_client.egg-info/dependency_links.txt +0 -0
  19. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/aas_http_client.egg-info/requires.txt +0 -0
  20. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/aas_http_client.egg-info/top_level.txt +0 -0
  21. {aas_http_client-0.3.1 → aas_http_client-0.3.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aas-http-client
3
- Version: 0.3.1
3
+ Version: 0.3.3
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
@@ -4,6 +4,7 @@ import json
4
4
  import logging
5
5
  import time
6
6
  from pathlib import Path
7
+ from typing import Any
7
8
 
8
9
  import basyx.aas.adapter.json
9
10
  import basyx.aas.adapter.json.json_serialization as js
@@ -26,7 +27,7 @@ STATUS_CODE_404 = 404
26
27
  HEADERS = {"Content-Type": "application/json"}
27
28
 
28
29
 
29
- def log_response_errors(response: Response):
30
+ def log_response_errors(response: Response): # noqa: C901
30
31
  """Create error messages from the response and log them.
31
32
 
32
33
  :param response: response
@@ -561,6 +562,32 @@ class AasHttpClient(BaseModel):
561
562
 
562
563
  return True
563
564
 
565
+ def patch_submodel_element_by_path_value_only_submodel_repo(self, submodel_id: str, submodel_element_path: str, value: str) -> bool:
566
+ """Updates the value of an existing SubmodelElement.
567
+
568
+ :param submodel_id: Encoded ID of the Submodel to update submodel element for
569
+ :param submodel_element_path: Path of the Submodel element to update
570
+ :param value: Submodel element value to update as string
571
+ :return: True if the patch was successful, False otherwise
572
+ """
573
+ decoded_submodel_id: str = decode_base_64(submodel_id)
574
+
575
+ url = f"{self.base_url}/submodels/{decoded_submodel_id}/submodel-elements/{submodel_element_path}/$value"
576
+
577
+ try:
578
+ response = self._session.patch(url, headers=HEADERS, json=value, timeout=self.time_out)
579
+ logger.debug(f"Call REST API url '{response.url}'")
580
+
581
+ if response.status_code != STATUS_CODE_204:
582
+ log_response_errors(response)
583
+ return False
584
+
585
+ except requests.exceptions.RequestException as e:
586
+ logger.error(f"Error call REST API: {e}")
587
+ return False
588
+
589
+ return True
590
+
564
591
 
565
592
  # endregion
566
593
 
@@ -577,19 +604,19 @@ def create_client_by_url(
577
604
  connection_time_out: int = 60,
578
605
  ssl_verify: str = True, # noqa: FBT002
579
606
  ) -> AasHttpClient | None:
580
- """Create a AAS HTTP client from the given parameters.
607
+ """Create a HTTP client for a AAS server connection from the given parameters.
581
608
 
582
- :param base_url: base URL of the BaSyx server, e.g. "http://basyx_python_server:80/"_
583
- :param username: username for the BaSyx server interface client, defaults to ""_
584
- :param password: password for the BaSyx server interface client, defaults to ""_
609
+ :param base_url: Base URL of the AAS server, e.g. "http://basyx_python_server:80/"_
610
+ :param username: Username for the AAS server, defaults to ""_
611
+ :param password: Password for the AAS server, defaults to ""_
585
612
  :param http_proxy: http proxy URL, defaults to ""_
586
613
  :param https_proxy: https proxy URL, defaults to ""_
587
- :param time_out: timeout for the API calls, defaults to 200
588
- :param connection_time_out: timeout for the connection to the API, defaults to 60
589
- :param ssl_verify: whether to verify SSL certificates, defaults to True
590
- :return: An instance of AasHttpClient initialized with the provided parameters.
614
+ :param time_out: Timeout for the API calls, defaults to 200
615
+ :param connection_time_out: Timeout for the connection to the API, defaults to 60
616
+ :param ssl_verify: Whether to verify SSL certificates, defaults to True
617
+ :return: An instance of Http client initialized with the provided parameters.
591
618
  """
592
- logger.info(f"Create BaSyx server interface client from URL '{base_url}'")
619
+ logger.info(f"Create AAS server http client from URL '{base_url}'")
593
620
  config_dict: dict[str, str] = {}
594
621
  config_dict["base_url"] = base_url
595
622
  config_dict["username"] = username
@@ -598,19 +625,31 @@ def create_client_by_url(
598
625
  config_dict["time_out"] = time_out
599
626
  config_dict["connection_time_out"] = connection_time_out
600
627
  config_dict["ssl_verify"] = ssl_verify
601
- config_string = json.dumps(config_dict, indent=4)
628
+ return create_client_by_dict(config_dict, password)
629
+
630
+
631
+ def create_client_by_dict(configuration: dict, password: str = "") -> AasHttpClient | None:
632
+ """Create a HTTP client for a AAS server connection from the given configuration.
633
+
634
+ :param configuration: Dictionary containing the BaSyx server connection settings.
635
+ :param password: Password for the AAS server, defaults to ""_
636
+ :return: An instance of Http client initialized with the provided parameters.
637
+ """
638
+ logger.info(f"Create AAS server http client from configuration '{configuration}'")
639
+ config_string = json.dumps(configuration, indent=4)
640
+
602
641
  return _create_client(config_string, password)
603
642
 
604
643
 
605
644
  def create_client_by_config(config_file: Path, password: str = "") -> AasHttpClient | None:
606
- """Create a AAS HTTP client from the given parameters.
645
+ """Create a HTTP client for a AAS server connection from a given configuration file.
607
646
 
608
- :param config_file: Path to the configuration file containing the BaSyx server connection settings.
647
+ :param config_file: Path to the configuration file containing the AAS server connection settings.
609
648
  :param password: password for the BaSyx server interface client, defaults to ""_
610
- :return: An instance of HttpClient initialized with the provided parameters.
649
+ :return: An instance of Http client initialized with the provided parameters.
611
650
  """
612
651
  config_file = config_file.resolve()
613
- logger.info(f"Create AAS HTTP client from Configuration file '{config_file}'")
652
+ logger.info(f"Create AAS server http client from configuration file '{config_file}'")
614
653
  if not config_file.exists():
615
654
  config_string = "{}"
616
655
  logger.warning(f"Configuration file '{config_file}' not found. Using default configuration.")
@@ -623,8 +662,7 @@ def create_client_by_config(config_file: Path, password: str = "") -> AasHttpCli
623
662
 
624
663
  def _create_client(config_string: str, password) -> AasHttpClient | None:
625
664
  try:
626
- connection_settings = AasHttpClient.model_validate_json(config_string)
627
- client = AasHttpClient(**connection_settings.model_dump())
665
+ client = AasHttpClient.model_validate_json(config_string)
628
666
  except ValidationError as ve:
629
667
  raise ValidationError(f"Invalid BaSyx server connection file: {ve}") from ve
630
668
 
@@ -19,6 +19,20 @@ class SdkWrapper:
19
19
  _client: AasHttpClient = None
20
20
  base_url: str = ""
21
21
 
22
+ def __init__(self, config_string: str, password: str = ""):
23
+ """Initializes the wrapper with the given configuration.
24
+
25
+ :param config_string: Configuration string for the BaSyx server connection.
26
+ :param password: Password for the BaSyx server interface client, defaults to "".
27
+ """
28
+ client = _create_client(config_string, password)
29
+
30
+ if not client:
31
+ raise ValueError("Failed to create AAS HTTP client with the provided configuration.")
32
+
33
+ self._client = client
34
+ self.base_url = client.base_url
35
+
22
36
  # region shells
23
37
 
24
38
  def post_asset_administration_shell(self, aas: model.AssetAdministrationShell) -> model.AssetAdministrationShell | None:
@@ -261,11 +275,24 @@ class SdkWrapper:
261
275
  :return: Submodel element object or None if an error occurred
262
276
  """
263
277
  content: dict = self._client.get_submodel_element_by_path_submodel_repo(submodel_id, submodel_element_path)
278
+ print(content)
264
279
  return _to_object(content)
265
280
 
281
+ def patch_submodel_element_by_path_value_only_submodel_repo(self, submodel_id: str, submodel_element_path: str, value: str) -> bool:
282
+ """Updates the value of an existing SubmodelElement.
283
+
284
+ :param submodel_id: Encoded ID of the Submodel to update submodel element for
285
+ :param submodel_element_path: Path of the Submodel element to update
286
+ :param value: Submodel element value to update as string
287
+ :return: True if the patch was successful, False otherwise
288
+ """
289
+ return self._client.patch_submodel_element_by_path_value_only_submodel_repo(submodel_id, submodel_element_path, value)
290
+
266
291
 
267
292
  # endregion
268
293
 
294
+ # region utils
295
+
269
296
 
270
297
  def _to_object(content: dict) -> Any | None:
271
298
  try:
@@ -287,6 +314,8 @@ def _to_dict(object: Any) -> dict | None:
287
314
  return None
288
315
 
289
316
 
317
+ # endregion
318
+
290
319
  # region wrapper
291
320
 
292
321
 
@@ -300,7 +329,7 @@ def create_wrapper_by_url(
300
329
  connection_time_out: int = 60,
301
330
  ssl_verify: str = True, # noqa: FBT002
302
331
  ) -> SdkWrapper | None:
303
- """Create a wrapper for the BaSyx Python SDK from the given parameters.
332
+ """Create a wrapper for a AAS server connection from the given parameters.
304
333
 
305
334
  :param base_url: base URL of the BaSyx server, e.g. "http://basyx_python_server:80/"_
306
335
  :param username: username for the BaSyx server interface client, defaults to ""_
@@ -312,7 +341,7 @@ def create_wrapper_by_url(
312
341
  :param ssl_verify: whether to verify SSL certificates, defaults to True
313
342
  :return: An instance of SdkWrapper initialized with the provided parameters.
314
343
  """
315
- logger.info(f"Create BaSyx Python SDK wrapper from URL '{base_url}'")
344
+ logger.info(f"Create AAS server wrapper from URL '{base_url}'")
316
345
  config_dict: dict[str, str] = {}
317
346
  config_dict["base_url"] = base_url
318
347
  config_dict["username"] = username
@@ -321,43 +350,36 @@ def create_wrapper_by_url(
321
350
  config_dict["time_out"] = time_out
322
351
  config_dict["connection_time_out"] = connection_time_out
323
352
  config_dict["ssl_verify"] = ssl_verify
324
- config_string = json.dumps(config_dict, indent=4)
353
+ return create_wrapper_by_dict(config_dict, password)
325
354
 
326
- wrapper = SdkWrapper()
327
- client = _create_client(config_string, password)
328
355
 
329
- if not client:
330
- return None
356
+ def create_wrapper_by_dict(configuration: dict, password: str = "") -> SdkWrapper | None:
357
+ """Create a wrapper for a AAS server connection from the given configuration.
331
358
 
332
- wrapper._client = client
333
- wrapper.base_url = client.base_url
334
- return wrapper
359
+ :param config: Dictionary containing the BaSyx server connection settings.
360
+ :param password: Password for the BaSyx server interface client, defaults to "".
361
+ :return: An instance of SdkWrapper initialized with the provided parameters.
362
+ """
363
+ logger.info(f"Create AAS server wrapper from configuration '{configuration}'")
364
+ config_string = json.dumps(configuration, indent=4)
365
+ return SdkWrapper(config_string, password)
335
366
 
336
367
 
337
368
  def create_wrapper_by_config(config_file: Path, password: str = "") -> SdkWrapper | None:
338
- """Create a wrapper for the BaSyx Python SDK from the given parameters.
369
+ """Create a wrapper for a AAS server connection from a given configuration file.
339
370
 
340
371
  :param config_file: Path to the configuration file containing the BaSyx server connection settings.
341
372
  :param password: password for the BaSyx server interface client, defaults to ""_
342
373
  :return: An instance of SdkWrapper initialized with the provided parameters.
343
374
  """
344
- logger.info(f"Create BaSyx Python SDK wrapper from configuration file '{config_file}'")
375
+ logger.info(f"Create AAS wrapper client from configuration file '{config_file}'")
345
376
  if not config_file.exists():
346
377
  config_string = "{}"
347
378
  logger.warning(f"Configuration file '{config_file}' not found. Using default config.")
348
379
  else:
349
380
  config_string = config_file.read_text(encoding="utf-8")
350
381
  logger.debug(f"Configuration file '{config_file}' found.")
351
-
352
- wrapper = SdkWrapper()
353
- client = _create_client(config_string, password)
354
-
355
- if not client:
356
- return None
357
-
358
- wrapper._client = client
359
- wrapper.base_url = client.base_url
360
- return wrapper
382
+ return SdkWrapper(config_string, password)
361
383
 
362
384
 
363
385
  # endregion
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aas-http-client
3
- Version: 0.3.1
3
+ Version: 0.3.3
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "aas-http-client"
7
- version = "0.3.1"
7
+ version = "0.3.3"
8
8
  description = "Generic python HTTP client for communication with various types of AAS servers"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -1,6 +1,6 @@
1
1
  import pytest
2
2
  from pathlib import Path
3
- from aas_http_client.client import create_client_by_config, AasHttpClient
3
+ from aas_http_client.client import create_client_by_config, AasHttpClient, create_client_by_dict, create_client_by_url
4
4
  from basyx.aas import model
5
5
  import aas_http_client.utilities.model_builder as model_builder
6
6
  import json
@@ -13,9 +13,13 @@ PYTHON_SERVER_PORTS = [8080, 80]
13
13
  CONFIG_FILES = [
14
14
  "./tests/server_configs/test_dotnet_server_config.json",
15
15
  "./tests/server_configs/test_java_server_config.json",
16
- "./tests/server_configs/test_python_server_config.json",
16
+ "./tests/server_configs/test_python_server_config.json"
17
17
  ]
18
18
 
19
+ # CONFIG_FILES = [
20
+ # "./tests/server_configs/test_dotnet_server_config_local.json",
21
+ # ]
22
+
19
23
  @pytest.fixture(params=CONFIG_FILES, scope="module")
20
24
  def client(request) -> AasHttpClient:
21
25
  try:
@@ -35,9 +39,24 @@ def client(request) -> AasHttpClient:
35
39
  return client
36
40
 
37
41
  @pytest.fixture(scope="module")
38
- def shared_sme() -> model.Property:
42
+ def shared_sme_string() -> model.Property:
43
+ # create a Submodel
44
+ return model_builder.create_base_submodel_element_Property("sme_property_string", model.datatypes.String, "Sample String Value")
45
+
46
+ @pytest.fixture(scope="module")
47
+ def shared_sme_bool() -> model.Property:
39
48
  # create a Submodel
40
- return model_builder.create_base_submodel_element_Property("sme_http_client_unit_tests", model.datatypes.String, "Sample Value")
49
+ return model_builder.create_base_submodel_element_Property("sme_property_bool", model.datatypes.Boolean, True)
50
+
51
+ @pytest.fixture(scope="module")
52
+ def shared_sme_int() -> model.Property:
53
+ # create a Submodel
54
+ return model_builder.create_base_submodel_element_Property("sme_property_int", model.datatypes.Integer, 262)
55
+
56
+ @pytest.fixture(scope="module")
57
+ def shared_sme_float() -> model.Property:
58
+ # create a Submodel
59
+ return model_builder.create_base_submodel_element_Property("sme_property_float", model.datatypes.Float, 262.3)
41
60
 
42
61
  @pytest.fixture(scope="module")
43
62
  def shared_sm() -> model.Submodel:
@@ -54,6 +73,21 @@ def shared_aas(shared_sm: model.Submodel) -> model.AssetAdministrationShell:
54
73
 
55
74
  return aas
56
75
 
76
+ def test_000a_create_client_by_url(client: AasHttpClient):
77
+ base_url: str = client.base_url
78
+ new_client: AasHttpClient = create_client_by_url(base_url=base_url)
79
+ assert new_client is not None
80
+
81
+ def test_000b_create_client_by_dict(client: AasHttpClient):
82
+ base_url: str = client.base_url
83
+
84
+ config_dict: dict = {
85
+ "base_url": base_url
86
+ }
87
+
88
+ new_client: AasHttpClient = create_client_by_dict(configuration=config_dict)
89
+ assert new_client is not None
90
+
57
91
  def test_001a_connect(client: AasHttpClient):
58
92
  assert client is not None
59
93
 
@@ -262,7 +296,7 @@ def test_012_patch_submodel_by_id(client: AasHttpClient, shared_sm: model.Submod
262
296
  # NOTE: Basyx java and python server do not provide this endpoint
263
297
  assert not result
264
298
  else:
265
- assert result
299
+ assert result is True
266
300
 
267
301
  get_result = client.get_submodel_by_id(shared_sm.id)
268
302
  assert get_result is not None
@@ -319,7 +353,7 @@ def test_014_put_submodels_by_id(client: AasHttpClient, shared_sm: model.Submode
319
353
 
320
354
  result = client.put_submodels_by_id(shared_sm.id, sm_data)
321
355
 
322
- assert result
356
+ assert result is True
323
357
 
324
358
  get_result = client.get_submodel_by_id(shared_sm.id)
325
359
  assert get_result is not None
@@ -341,33 +375,186 @@ def test_015_get_all_submodel_elements_submodel_repository(client: AasHttpClient
341
375
  assert submodel_elements is not None
342
376
  assert len(submodel_elements.get("result", [])) == 0
343
377
 
344
- def test_016_post_submodel_element_submodel_repo(client: AasHttpClient, shared_sm: model.Submodel, shared_sme: model.Property):
345
- sme_data_string = json.dumps(shared_sme, cls=basyx.aas.adapter.json.AASToJsonEncoder)
378
+ def test_016a_post_submodel_element_submodel_repo(client: AasHttpClient, shared_sm: model.Submodel, shared_sme_string: model.Property):
379
+ sme_data_string = json.dumps(shared_sme_string, cls=basyx.aas.adapter.json.AASToJsonEncoder)
346
380
  sme_data = json.loads(sme_data_string)
347
381
 
348
382
  result = client.post_submodel_element_submodel_repo(shared_sm.id, sme_data)
349
383
 
350
384
  assert result is not None
351
- assert result.get("idShort", "") == shared_sme.id_short
352
- assert result.get("description", {})[0].get("text", "") == shared_sme.description.get("en", "")
353
- assert result.get("displayName", {})[0].get("text", "") == shared_sme.display_name.get("en", "")
385
+ assert result.get("idShort", "") == shared_sme_string.id_short
386
+ assert result.get("description", {})[0].get("text", "") == shared_sme_string.description.get("en", "")
387
+ assert result.get("displayName", {})[0].get("text", "") == shared_sme_string.display_name.get("en", "")
388
+ assert result.get("value", "") == shared_sme_string.value
354
389
 
355
390
  get_result = client.get_all_submodel_elements_submodel_repository(shared_sm.id)
356
391
 
357
392
  assert len(get_result.get("result", [])) == 1
358
393
 
359
- def test_017_get_submodel_element_by_path_submodel_repo(client: AasHttpClient, shared_sm: model.Submodel, shared_sme: model.Property):
360
- result = client.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme.id_short)
394
+ def test_016b_post_submodel_element_submodel_repo(client: AasHttpClient, shared_sm: model.Submodel, shared_sme_bool: model.Property):
395
+ sme_data_string = json.dumps(shared_sme_bool, cls=basyx.aas.adapter.json.AASToJsonEncoder)
396
+ sme_data = json.loads(sme_data_string)
397
+
398
+ result = client.post_submodel_element_submodel_repo(shared_sm.id, sme_data)
399
+
400
+ assert result is not None
401
+ assert result.get("idShort", "") == shared_sme_bool.id_short
402
+ assert result.get("description", {})[0].get("text", "") == shared_sme_bool.description.get("en", "")
403
+ assert result.get("displayName", {})[0].get("text", "") == shared_sme_bool.display_name.get("en", "")
404
+ assert json.loads(result.get("value", "").lower()) == shared_sme_bool.value
405
+
406
+ get_result = client.get_all_submodel_elements_submodel_repository(shared_sm.id)
407
+
408
+ assert len(get_result.get("result", [])) == 2
409
+
410
+ def test_016c_post_submodel_element_submodel_repo(client: AasHttpClient, shared_sm: model.Submodel, shared_sme_int: model.Property):
411
+ sme_data_string = json.dumps(shared_sme_int, cls=basyx.aas.adapter.json.AASToJsonEncoder)
412
+ sme_data = json.loads(sme_data_string)
413
+
414
+ result = client.post_submodel_element_submodel_repo(shared_sm.id, sme_data)
415
+
416
+ assert result is not None
417
+ assert result.get("idShort", "") == shared_sme_int.id_short
418
+ assert result.get("description", {})[0].get("text", "") == shared_sme_int.description.get("en", "")
419
+ assert result.get("displayName", {})[0].get("text", "") == shared_sme_int.display_name.get("en", "")
420
+ assert int(result.get("value", "")) == shared_sme_int.value
421
+
422
+ get_result = client.get_all_submodel_elements_submodel_repository(shared_sm.id)
423
+
424
+ assert len(get_result.get("result", [])) == 3
425
+
426
+ def test_016d_post_submodel_element_submodel_repo(client: AasHttpClient, shared_sm: model.Submodel, shared_sme_float: model.Property):
427
+ sme_data_string = json.dumps(shared_sme_float, cls=basyx.aas.adapter.json.AASToJsonEncoder)
428
+ sme_data = json.loads(sme_data_string)
429
+
430
+ result = client.post_submodel_element_submodel_repo(shared_sm.id, sme_data)
431
+
432
+ assert result is not None
433
+ assert result.get("idShort", "") == shared_sme_float.id_short
434
+ assert result.get("description", {})[0].get("text", "") == shared_sme_float.description.get("en", "")
435
+ assert result.get("displayName", {})[0].get("text", "") == shared_sme_float.display_name.get("en", "")
436
+ assert float(result.get("value", "")) == shared_sme_float.value
437
+
438
+ get_result = client.get_all_submodel_elements_submodel_repository(shared_sm.id)
439
+
440
+ assert len(get_result.get("result", [])) == 4
441
+
442
+ def test_017a_get_submodel_element_by_path_submodel_repo(client: AasHttpClient, shared_sm: model.Submodel, shared_sme_string: model.Property):
443
+ result = client.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_string.id_short)
361
444
 
362
445
  assert result is not None
363
- assert result.get("idShort", "") == shared_sme.id_short
364
- assert result.get("description", {})[0].get("text", "") == shared_sme.description.get("en", "")
365
- assert result.get("displayName", {})[0].get("text", "") == shared_sme.display_name.get("en", "")
446
+ assert result.get("idShort", "") == shared_sme_string.id_short
447
+ assert result.get("description", {})[0].get("text", "") == shared_sme_string.description.get("en", "")
448
+ assert result.get("displayName", {})[0].get("text", "") == shared_sme_string.display_name.get("en", "")
449
+ assert result.get("value", "") == shared_sme_string.value
450
+
451
+ def test_017b_get_submodel_element_by_path_submodel_repo(client: AasHttpClient, shared_sm: model.Submodel, shared_sme_bool: model.Property):
452
+ result = client.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_bool.id_short)
453
+
454
+ assert result is not None
455
+ assert result.get("idShort", "") == shared_sme_bool.id_short
456
+ assert result.get("description", {})[0].get("text", "") == shared_sme_bool.description.get("en", "")
457
+ assert result.get("displayName", {})[0].get("text", "") == shared_sme_bool.display_name.get("en", "")
458
+ assert json.loads(result.get("value", "").lower()) == shared_sme_bool.value
459
+
460
+ def test_017c_get_submodel_element_by_path_submodel_repo(client: AasHttpClient, shared_sm: model.Submodel, shared_sme_int: model.Property):
461
+ result = client.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_int.id_short)
462
+
463
+ assert result is not None
464
+ assert result.get("idShort", "") == shared_sme_int.id_short
465
+ assert result.get("description", {})[0].get("text", "") == shared_sme_int.description.get("en", "")
466
+ assert result.get("displayName", {})[0].get("text", "") == shared_sme_int.display_name.get("en", "")
467
+ assert int(result.get("value", "")) == shared_sme_int.value
468
+
469
+ def test_017d_get_submodel_element_by_path_submodel_repo(client: AasHttpClient, shared_sm: model.Submodel, shared_sme_float: model.Property):
470
+ result = client.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_float.id_short)
471
+
472
+ assert result is not None
473
+ assert result.get("idShort", "") == shared_sme_float.id_short
474
+ assert result.get("description", {})[0].get("text", "") == shared_sme_float.description.get("en", "")
475
+ assert result.get("displayName", {})[0].get("text", "") == shared_sme_float.display_name.get("en", "")
476
+ assert float(result.get("value", "")) == shared_sme_float.value
477
+
478
+ def test_018a_patch_submodel_element_by_path_value_only_submodel_repo(client: AasHttpClient, shared_sm: model.Submodel, shared_sme_string: model.Property):
479
+ new_value = "Patched String Value"
480
+ result = client.patch_submodel_element_by_path_value_only_submodel_repo(shared_sm.id, shared_sme_string.id_short, new_value)
481
+
482
+ parsed = urlparse(client.base_url)
483
+ if int(parsed.port) in PYTHON_SERVER_PORTS:
484
+ # NOTE: python server do not provide this endpoint
485
+ assert result is False
486
+ else:
487
+ assert result is True
488
+
489
+ get_result = client.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_string.id_short)
490
+
491
+ assert get_result is not None
492
+ assert get_result.get("idShort", "") == shared_sme_string.id_short
493
+ assert get_result.get("value", "") == new_value
494
+ assert get_result.get("description", {})[0].get("text", "") == shared_sme_string.description.get("en", "")
495
+ assert get_result.get("displayName", {})[0].get("text", "") == shared_sme_string.display_name.get("en", "")
496
+
497
+ def test_018b_patch_submodel_element_by_path_value_only_submodel_repo(client: AasHttpClient, shared_sm: model.Submodel, shared_sme_bool: model.Property):
498
+ new_value = "false"
499
+ result = client.patch_submodel_element_by_path_value_only_submodel_repo(shared_sm.id, shared_sme_bool.id_short, new_value)
500
+
501
+ parsed = urlparse(client.base_url)
502
+ if int(parsed.port) in PYTHON_SERVER_PORTS:
503
+ # NOTE: python server do not provide this endpoint
504
+ assert result is False
505
+ else:
506
+ assert result is True
507
+
508
+ get_result = client.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_bool.id_short)
509
+
510
+ assert get_result is not None
511
+ assert get_result.get("idShort", "") == shared_sme_bool.id_short
512
+ assert json.loads(get_result.get("value", "").lower()) == json.loads(new_value)
513
+ assert get_result.get("description", {})[0].get("text", "") == shared_sme_bool.description.get("en", "")
514
+ assert get_result.get("displayName", {})[0].get("text", "") == shared_sme_bool.display_name.get("en", "")
515
+
516
+ def test_018c_patch_submodel_element_by_path_value_only_submodel_repo(client: AasHttpClient, shared_sm: model.Submodel, shared_sme_int: model.Property):
517
+ new_value = "263"
518
+ result = client.patch_submodel_element_by_path_value_only_submodel_repo(shared_sm.id, shared_sme_int.id_short, new_value)
519
+
520
+ parsed = urlparse(client.base_url)
521
+ if int(parsed.port) in PYTHON_SERVER_PORTS:
522
+ # NOTE: python server do not provide this endpoint
523
+ assert result is False
524
+ else:
525
+ assert result is True
526
+
527
+ get_result = client.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_int.id_short)
528
+
529
+ assert get_result is not None
530
+ assert get_result.get("idShort", "") == shared_sme_int.id_short
531
+ assert int(get_result.get("value", "")) == int(new_value)
532
+ assert get_result.get("description", {})[0].get("text", "") == shared_sme_int.description.get("en", "")
533
+ assert get_result.get("displayName", {})[0].get("text", "") == shared_sme_int.display_name.get("en", "")
534
+
535
+ def test_018d_patch_submodel_element_by_path_value_only_submodel_repo(client: AasHttpClient, shared_sm: model.Submodel, shared_sme_float: model.Property):
536
+ new_value = "262.1"
537
+ result = client.patch_submodel_element_by_path_value_only_submodel_repo(shared_sm.id, shared_sme_float.id_short, new_value)
538
+
539
+ parsed = urlparse(client.base_url)
540
+ if int(parsed.port) in PYTHON_SERVER_PORTS:
541
+ # NOTE: python server do not provide this endpoint
542
+ assert result is False
543
+ else:
544
+ assert result is True
545
+
546
+ get_result = client.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_float.id_short)
547
+
548
+ assert get_result is not None
549
+ assert get_result.get("idShort", "") == shared_sme_float.id_short
550
+ assert float(get_result.get("value", "")) == float(new_value)
551
+ assert get_result.get("description", {})[0].get("text", "") == shared_sme_float.description.get("en", "")
552
+ assert get_result.get("displayName", {})[0].get("text", "") == shared_sme_float.display_name.get("en", "")
366
553
 
367
554
  def test_098_delete_asset_administration_shell_by_id(client: AasHttpClient, shared_aas: model.AssetAdministrationShell):
368
555
  result = client.delete_asset_administration_shell_by_id(shared_aas.id)
369
556
 
370
- assert result
557
+ assert result is True
371
558
 
372
559
  get_result = client.get_all_asset_administration_shells()
373
560
  assert get_result is not None
@@ -377,7 +564,7 @@ def test_098_delete_asset_administration_shell_by_id(client: AasHttpClient, shar
377
564
  def test_099_delete_submodel_by_id(client: AasHttpClient, shared_sm: model.Submodel):
378
565
  result = client.delete_submodel_by_id(shared_sm.id)
379
566
 
380
- assert result
567
+ assert result is True
381
568
 
382
569
  get_result = client.get_all_submodels()
383
570
  assert get_result is not None
@@ -1,9 +1,10 @@
1
1
  import pytest
2
2
  from pathlib import Path
3
- from aas_http_client.wrapper.sdk_wrapper import create_wrapper_by_config, SdkWrapper
3
+ from aas_http_client.wrapper.sdk_wrapper import create_wrapper_by_config, SdkWrapper, create_wrapper_by_dict, create_wrapper_by_url
4
4
  from basyx.aas import model
5
5
  import aas_http_client.utilities.model_builder as model_builder
6
6
  from urllib.parse import urlparse
7
+ import json
7
8
 
8
9
  JAVA_SERVER_PORTS = [8075]
9
10
  PYTHON_SERVER_PORTS = [8080, 80]
@@ -15,6 +16,10 @@ CONFIG_FILES = [
15
16
  "./tests/server_configs/test_python_server_config.json"
16
17
  ]
17
18
 
19
+ # CONFIG_FILES = [
20
+ # "./tests/server_configs/test_dotnet_server_config_local.json",
21
+ # ]
22
+
18
23
  @pytest.fixture(params=CONFIG_FILES, scope="module")
19
24
  def wrapper(request) -> SdkWrapper:
20
25
  try:
@@ -34,9 +39,24 @@ def wrapper(request) -> SdkWrapper:
34
39
  return wrapper
35
40
 
36
41
  @pytest.fixture(scope="module")
37
- def shared_sme() -> model.Property:
42
+ def shared_sme_string() -> model.Property:
43
+ # create a Submodel
44
+ return model_builder.create_base_submodel_element_Property("sme_property_string", model.datatypes.String, "Sample String Value")
45
+
46
+ @pytest.fixture(scope="module")
47
+ def shared_sme_bool() -> model.Property:
48
+ # create a Submodel
49
+ return model_builder.create_base_submodel_element_Property("sme_property_bool", model.datatypes.Boolean, True)
50
+
51
+ @pytest.fixture(scope="module")
52
+ def shared_sme_int() -> model.Property:
53
+ # create a Submodel
54
+ return model_builder.create_base_submodel_element_Property("sme_property_int", model.datatypes.Integer, 262)
55
+
56
+ @pytest.fixture(scope="module")
57
+ def shared_sme_float() -> model.Property:
38
58
  # create a Submodel
39
- return model_builder.create_base_submodel_element_Property("sme_http_client_unit_tests", model.datatypes.String, "Sample Value")
59
+ return model_builder.create_base_submodel_element_Property("sme_property_float", model.datatypes.Float, 262.3)
40
60
 
41
61
  @pytest.fixture(scope="module")
42
62
  def shared_sm() -> model.Submodel:
@@ -55,6 +75,21 @@ def shared_aas(shared_sm: model.Submodel) -> model.AssetAdministrationShell:
55
75
 
56
76
  return aas
57
77
 
78
+ def test_000a_create_wrapper_by_url(wrapper: SdkWrapper):
79
+ base_url: str = wrapper.base_url
80
+ new_client: SdkWrapper = create_wrapper_by_url(base_url=base_url)
81
+ assert new_client is not None
82
+
83
+ def test_000b_create_wrapper_by_dict(wrapper: SdkWrapper):
84
+ base_url: str = wrapper.base_url
85
+
86
+ config_dict: dict = {
87
+ "base_url": base_url
88
+ }
89
+
90
+ new_client: SdkWrapper = create_wrapper_by_dict(configuration=config_dict)
91
+ assert new_client is not None
92
+
58
93
  def test_001_connect(wrapper: SdkWrapper):
59
94
  assert wrapper is not None
60
95
 
@@ -292,42 +327,217 @@ def test_015_get_all_submodel_elements_submodel_repository(wrapper: SdkWrapper,
292
327
  assert submodel_elements is not None
293
328
  assert len(submodel_elements) == 0
294
329
 
295
- def test_016_post_submodel_element_submodel_repo(wrapper: SdkWrapper, shared_sm: model.Submodel, shared_sme: model.Property):
296
- submodel_element = wrapper.post_submodel_element_submodel_repo(shared_sm.id, shared_sme)
330
+ def test_016a_post_submodel_element_submodel_repo(wrapper: SdkWrapper, shared_sm: model.Submodel, shared_sme_string: model.Property):
331
+ submodel_element = wrapper.post_submodel_element_submodel_repo(shared_sm.id, shared_sme_string)
297
332
 
298
333
  assert submodel_element is not None
299
334
 
300
- parsed = urlparse(wrapper.base_url)
301
- if int(parsed.port) in DOTNET_SERVER_PORTS:
302
- # NOTE: dotNet server provides a wrong representation of submodel elements
303
- return
304
-
305
335
  assert isinstance(submodel_element, model.Property)
336
+ property: model.Property = submodel_element
337
+ assert property.value == shared_sme_string.value
306
338
 
307
- assert submodel_element.id_short == shared_sme.id_short
308
- assert submodel_element.description.get("en", "") == shared_sme.description.get("en", "")
309
- assert submodel_element.display_name.get("en", "") == shared_sme.display_name.get("en", "")
339
+ assert submodel_element.id_short == shared_sme_string.id_short
340
+ assert submodel_element.description.get("en", "") == shared_sme_string.description.get("en", "")
341
+ assert submodel_element.display_name.get("en", "") == shared_sme_string.display_name.get("en", "")
342
+ assert submodel_element.value == shared_sme_string.value
310
343
 
311
344
  submodel_elements = wrapper.get_all_submodel_elements_submodel_repository(shared_sm.id)
312
345
 
313
346
  assert submodel_elements is not None
314
347
  assert len(submodel_elements) == 1
315
348
 
316
- def test_017_get_submodel_element_by_path_submodel_repo(wrapper: SdkWrapper, shared_sm: model.Submodel, shared_sme: model.Property):
317
- submodel_element = wrapper.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme.id_short)
349
+ def test_016b_post_submodel_element_submodel_repo(wrapper: SdkWrapper, shared_sm: model.Submodel, shared_sme_bool: model.Property):
350
+ submodel_element = wrapper.post_submodel_element_submodel_repo(shared_sm.id, shared_sme_bool)
318
351
 
319
352
  assert submodel_element is not None
320
353
 
321
- parsed = urlparse(wrapper.base_url)
322
- if int(parsed.port) in DOTNET_SERVER_PORTS:
323
- # NOTE: dotNet server provides a wrong representation of submodel elements
324
- return
354
+ assert isinstance(submodel_element, model.Property)
355
+ property: model.Property = submodel_element
356
+ assert property.value == shared_sme_bool.value
357
+
358
+ assert submodel_element.id_short == shared_sme_bool.id_short
359
+ assert submodel_element.description.get("en", "") == shared_sme_bool.description.get("en", "")
360
+ assert submodel_element.display_name.get("en", "") == shared_sme_bool.display_name.get("en", "")
361
+ assert submodel_element.value == shared_sme_bool.value
362
+
363
+ submodel_elements = wrapper.get_all_submodel_elements_submodel_repository(shared_sm.id)
364
+
365
+ assert submodel_elements is not None
366
+ assert len(submodel_elements) == 2
367
+
368
+ def test_016c_post_submodel_element_submodel_repo(wrapper: SdkWrapper, shared_sm: model.Submodel, shared_sme_int: model.Property):
369
+ submodel_element = wrapper.post_submodel_element_submodel_repo(shared_sm.id, shared_sme_int)
370
+
371
+ assert submodel_element is not None
372
+
373
+ assert isinstance(submodel_element, model.Property)
374
+ property: model.Property = submodel_element
375
+ assert property.value == shared_sme_int.value
376
+
377
+ assert submodel_element.id_short == shared_sme_int.id_short
378
+ assert submodel_element.description.get("en", "") == shared_sme_int.description.get("en", "")
379
+ assert submodel_element.display_name.get("en", "") == shared_sme_int.display_name.get("en", "")
380
+ assert submodel_element.value == shared_sme_int.value
381
+
382
+ submodel_elements = wrapper.get_all_submodel_elements_submodel_repository(shared_sm.id)
383
+
384
+ assert submodel_elements is not None
385
+ assert len(submodel_elements) == 3
386
+
387
+ def test_016d_post_submodel_element_submodel_repo(wrapper: SdkWrapper, shared_sm: model.Submodel, shared_sme_float: model.Property):
388
+ submodel_element = wrapper.post_submodel_element_submodel_repo(shared_sm.id, shared_sme_float)
389
+
390
+ assert submodel_element is not None
391
+
392
+ assert isinstance(submodel_element, model.Property)
393
+ property: model.Property = submodel_element
394
+ assert property.value == shared_sme_float.value
395
+
396
+ assert submodel_element.id_short == shared_sme_float.id_short
397
+ assert submodel_element.description.get("en", "") == shared_sme_float.description.get("en", "")
398
+ assert submodel_element.display_name.get("en", "") == shared_sme_float.display_name.get("en", "")
399
+ assert submodel_element.value == shared_sme_float.value
400
+
401
+ submodel_elements = wrapper.get_all_submodel_elements_submodel_repository(shared_sm.id)
402
+
403
+ assert submodel_elements is not None
404
+ assert len(submodel_elements) == 4
405
+
406
+ def test_017a_get_submodel_element_by_path_submodel_repo(wrapper: SdkWrapper, shared_sm: model.Submodel, shared_sme_string: model.Property):
407
+ submodel_element = wrapper.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_string.id_short)
408
+
409
+ assert submodel_element is not None
410
+
411
+ assert isinstance(submodel_element, model.Property)
412
+
413
+ assert submodel_element.id_short == shared_sme_string.id_short
414
+ assert submodel_element.description.get("en", "") == shared_sme_string.description.get("en", "")
415
+ assert submodel_element.display_name.get("en", "") == shared_sme_string.display_name.get("en", "")
416
+ assert submodel_element.value == shared_sme_string.value
417
+
418
+ def test_017b_get_submodel_element_by_path_submodel_repo(wrapper: SdkWrapper, shared_sm: model.Submodel, shared_sme_bool: model.Property):
419
+ submodel_element = wrapper.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_bool.id_short)
420
+
421
+ assert submodel_element is not None
422
+
423
+ assert isinstance(submodel_element, model.Property)
424
+
425
+ assert submodel_element.id_short == shared_sme_bool.id_short
426
+ assert submodel_element.description.get("en", "") == shared_sme_bool.description.get("en", "")
427
+ assert submodel_element.display_name.get("en", "") == shared_sme_bool.display_name.get("en", "")
428
+ assert submodel_element.value == shared_sme_bool.value
429
+
430
+ def test_017c_get_submodel_element_by_path_submodel_repo(wrapper: SdkWrapper, shared_sm: model.Submodel, shared_sme_int: model.Property):
431
+ submodel_element = wrapper.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_int.id_short)
432
+
433
+ assert submodel_element is not None
434
+
435
+ assert isinstance(submodel_element, model.Property)
436
+
437
+ assert submodel_element.id_short == shared_sme_int.id_short
438
+ assert submodel_element.description.get("en", "") == shared_sme_int.description.get("en", "")
439
+ assert submodel_element.display_name.get("en", "") == shared_sme_int.display_name.get("en", "")
440
+ assert submodel_element.value == shared_sme_int.value
441
+
442
+ def test_017d_get_submodel_element_by_path_submodel_repo(wrapper: SdkWrapper, shared_sm: model.Submodel, shared_sme_float: model.Property):
443
+ submodel_element = wrapper.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_float.id_short)
444
+
445
+ assert submodel_element is not None
325
446
 
326
447
  assert isinstance(submodel_element, model.Property)
327
448
 
328
- assert submodel_element.id_short == shared_sme.id_short
329
- assert submodel_element.description.get("en", "") == shared_sme.description.get("en", "")
330
- assert submodel_element.display_name.get("en", "") == shared_sme.display_name.get("en", "")
449
+ assert submodel_element.id_short == shared_sme_float.id_short
450
+ assert submodel_element.description.get("en", "") == shared_sme_float.description.get("en", "")
451
+ assert submodel_element.display_name.get("en", "") == shared_sme_float.display_name.get("en", "")
452
+ assert submodel_element.value == shared_sme_float.value
453
+
454
+ def test_018a_patch_submodel_element_by_path_value_only_submodel_repo(wrapper: SdkWrapper, shared_sm: model.Submodel, shared_sme_string: model.Property):
455
+ new_value = "Patched String Value"
456
+ result = wrapper.patch_submodel_element_by_path_value_only_submodel_repo(shared_sm.id, shared_sme_string.id_short, new_value)
457
+
458
+ parsed = urlparse(wrapper.base_url)
459
+ if int(parsed.port) in PYTHON_SERVER_PORTS:
460
+ # NOTE: python server do not provide this endpoint
461
+ assert result is False
462
+ else:
463
+ assert result is True
464
+
465
+ submodel_element = wrapper.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_string.id_short)
466
+
467
+ assert submodel_element is not None
468
+ assert submodel_element.id_short == shared_sme_string.id_short
469
+ assert submodel_element.description.get("en", "") == shared_sme_string.description.get("en", "")
470
+ assert submodel_element.display_name.get("en", "") == shared_sme_string.display_name.get("en", "")
471
+
472
+ assert isinstance(submodel_element, model.Property)
473
+ property: model.Property = submodel_element
474
+ assert property.value == new_value
475
+
476
+ def test_018b_patch_submodel_element_by_path_value_only_submodel_repo(wrapper: SdkWrapper, shared_sm: model.Submodel, shared_sme_bool: model.Property):
477
+ new_value = "false"
478
+ result = wrapper.patch_submodel_element_by_path_value_only_submodel_repo(shared_sm.id, shared_sme_bool.id_short, new_value)
479
+
480
+ parsed = urlparse(wrapper.base_url)
481
+ if int(parsed.port) in PYTHON_SERVER_PORTS:
482
+ # NOTE: python server do not provide this endpoint
483
+ assert result is False
484
+ else:
485
+ assert result is True
486
+
487
+ submodel_element = wrapper.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_bool.id_short)
488
+
489
+ assert submodel_element is not None
490
+ assert submodel_element.id_short == shared_sme_bool.id_short
491
+ assert submodel_element.description.get("en", "") == shared_sme_bool.description.get("en", "")
492
+ assert submodel_element.display_name.get("en", "") == shared_sme_bool.display_name.get("en", "")
493
+
494
+ assert isinstance(submodel_element, model.Property)
495
+ property: model.Property = submodel_element
496
+ assert property.value == json.loads(new_value)
497
+
498
+ def test_018c_patch_submodel_element_by_path_value_only_submodel_repo(wrapper: SdkWrapper, shared_sm: model.Submodel, shared_sme_int: model.Property):
499
+ new_value = "263"
500
+ result = wrapper.patch_submodel_element_by_path_value_only_submodel_repo(shared_sm.id, shared_sme_int.id_short, new_value)
501
+
502
+ parsed = urlparse(wrapper.base_url)
503
+ if int(parsed.port) in PYTHON_SERVER_PORTS:
504
+ # NOTE: python server do not provide this endpoint
505
+ assert result is False
506
+ else:
507
+ assert result is True
508
+
509
+ submodel_element = wrapper.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_int.id_short)
510
+
511
+ assert submodel_element is not None
512
+ assert submodel_element.id_short == shared_sme_int.id_short
513
+ assert submodel_element.description.get("en", "") == shared_sme_int.description.get("en", "")
514
+ assert submodel_element.display_name.get("en", "") == shared_sme_int.display_name.get("en", "")
515
+
516
+ assert isinstance(submodel_element, model.Property)
517
+ property: model.Property = submodel_element
518
+ assert property.value == int(new_value)
519
+
520
+ def test_018d_patch_submodel_element_by_path_value_only_submodel_repo(wrapper: SdkWrapper, shared_sm: model.Submodel, shared_sme_float: model.Property):
521
+ new_value = "262.1"
522
+ result = wrapper.patch_submodel_element_by_path_value_only_submodel_repo(shared_sm.id, shared_sme_float.id_short, new_value)
523
+
524
+ parsed = urlparse(wrapper.base_url)
525
+ if int(parsed.port) in PYTHON_SERVER_PORTS:
526
+ # NOTE: python server do not provide this endpoint
527
+ assert result is False
528
+ else:
529
+ assert result is True
530
+
531
+ submodel_element = wrapper.get_submodel_element_by_path_submodel_repo(shared_sm.id, shared_sme_float.id_short)
532
+
533
+ assert submodel_element is not None
534
+ assert submodel_element.id_short == shared_sme_float.id_short
535
+ assert submodel_element.description.get("en", "") == shared_sme_float.description.get("en", "")
536
+ assert submodel_element.display_name.get("en", "") == shared_sme_float.display_name.get("en", "")
537
+
538
+ assert isinstance(submodel_element, model.Property)
539
+ property: model.Property = submodel_element
540
+ assert property.value == float(new_value)
331
541
 
332
542
  def test_098_delete_asset_administration_shell_by_id(wrapper: SdkWrapper, shared_aas: model.AssetAdministrationShell):
333
543
  result = wrapper.delete_asset_administration_shell_by_id(shared_aas.id)
File without changes