From c88ed71c932372d3e47c2c01a45f05aecd07145c Mon Sep 17 00:00:00 2001 From: Sercan Sahin Date: Tue, 2 Dec 2025 17:08:33 +0100 Subject: [PATCH 1/4] implement primitive data types for API --- sdk/basyx/aas/model/base.py | 2 +- server/app/interfaces/_string_constraints.py | 64 ++++++++++++++++++++ server/app/interfaces/base.py | 14 ++++- server/app/interfaces/repository.py | 12 ++-- 4 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 server/app/interfaces/_string_constraints.py diff --git a/sdk/basyx/aas/model/base.py b/sdk/basyx/aas/model/base.py index 35ccad5a..675a9f32 100644 --- a/sdk/basyx/aas/model/base.py +++ b/sdk/basyx/aas/model/base.py @@ -28,7 +28,7 @@ BlobType = bytes # The following string aliases are constrained by the decorator functions defined in the string_constraints module, -# wherever they are used for an instance attributes. +# wherever they are used for an instances attributes. ContentType = str # any mimetype as in RFC2046 Identifier = str LabelType = str diff --git a/server/app/interfaces/_string_constraints.py b/server/app/interfaces/_string_constraints.py new file mode 100644 index 00000000..65a35fe0 --- /dev/null +++ b/server/app/interfaces/_string_constraints.py @@ -0,0 +1,64 @@ +# Copyright (c) 2025 the Eclipse BaSyx Authors +# +# This program and the accompanying materials are made available under the terms of the MIT License, available in +# the LICENSE file of this project. +# +# SPDX-License-Identifier: MIT +""" +This module implements constraint functions for the listed constrained string types. +All types are constrained in length (min and max). + +.. warning:: + This module is intended for internal use only. + +The following types aliased in the :mod:`~server.app.interfaces.base` module are constrained: + +- :class:`~server.app.interfaces.base.CodeType` +- :class:`~server.app.interfaces.base.ShortIdType` +- :class:`~server.app.interfaces.base.LocatorType` +- :class:`~server.app.interfaces.base.TextType` +- :class:`~server.app.interfaces.base.SchemeType` +""" + +from typing import Callable, Type +from basyx.aas.model._string_constraints import check, constrain_attr, _T + + +def check_code_type(value: str, type_name: str = "CodeType") -> None: + return check(value, type_name, 1, 32) + + +def check_short_id_type(value: str, type_name: str = "ShortIdType") -> None: + return check(value, type_name, 1, 128) + + +def check_locator_type(value: str, type_name: str = "LocatorType") -> None: + return check(value, type_name, 1, 2048) + + +def check_text_type(value: str, type_name: str = "TextType") -> None: + return check(value, type_name, 1, 2048) + + +def check_scheme_type(value: str, type_name: str = "SchemeType") -> None: + return check(value, type_name, 1, 128) + + +def constrain_code_type(pub_attr_name: str) -> Callable[[Type[_T]], Type[_T]]: + return constrain_attr(pub_attr_name, check_code_type) + + +def constrain_short_id_type(pub_attr_name: str) -> Callable[[Type[_T]], Type[_T]]: + return constrain_attr(pub_attr_name, check_short_id_type) + + +def constrain_locator_type(pub_attr_name: str) -> Callable[[Type[_T]], Type[_T]]: + return constrain_attr(pub_attr_name, check_locator_type) + + +def constrain_text_type(pub_attr_name: str) -> Callable[[Type[_T]], Type[_T]]: + return constrain_attr(pub_attr_name, check_text_type) + + +def constrain_scheme_type(pub_attr_name: str) -> Callable[[Type[_T]], Type[_T]]: + return constrain_attr(pub_attr_name, check_scheme_type) diff --git a/server/app/interfaces/base.py b/server/app/interfaces/base.py index 2f23b562..c0f9248d 100644 --- a/server/app/interfaces/base.py +++ b/server/app/interfaces/base.py @@ -20,6 +20,8 @@ from werkzeug.exceptions import NotFound, BadRequest from werkzeug.routing import MapAdapter +from . import _string_constraints + from basyx.aas import model from basyx.aas.adapter._generic import XML_NS_MAP from basyx.aas.adapter.json import StrictStrippedAASFromJsonDecoder, StrictAASFromJsonDecoder, AASToJsonEncoder @@ -27,6 +29,13 @@ from basyx.aas.model import AbstractObjectStore from util.converters import base64url_decode +# The following string aliases are constrained by the decorator functions defined in the string_constraints module, +# wherever they are used for an instances attributes. +CodeType = str +ShortIdType = str +LocatorType = str +TextType = str +SchemeType = str T = TypeVar("T") @@ -43,10 +52,11 @@ def __str__(self): return self.name.capitalize() +@_string_constraints.constrain_code_type("code") class Message: - def __init__(self, code: str, text: str, message_type: MessageType = MessageType.UNDEFINED, + def __init__(self, code: CodeType, text: str, message_type: MessageType = MessageType.UNDEFINED, timestamp: Optional[datetime.datetime] = None): - self.code: str = code + self.code: CodeType = code self.text: str = text self.message_type: MessageType = message_type self.timestamp: datetime.datetime = timestamp if timestamp is not None \ diff --git a/server/app/interfaces/repository.py b/server/app/interfaces/repository.py index 03a3974c..9db23659 100644 --- a/server/app/interfaces/repository.py +++ b/server/app/interfaces/repository.py @@ -8,22 +8,20 @@ This module implements the "Specification of the Asset Administration Shell Part 2 Application Programming Interfaces". However, several features and routes are currently not supported: -1. Correlation ID: Not implemented because it was deemed unnecessary for this server. - -2. Extent Parameter (`withBlobValue/withoutBlobValue`): +1. Extent Parameter (`withBlobValue/withoutBlobValue`): Not implemented due to the lack of support in JSON/XML serialization. -3. Route `/shells/{aasIdentifier}/asset-information/thumbnail`: Not implemented because the specification lacks clarity. +2. Route `/shells/{aasIdentifier}/asset-information/thumbnail`: Not implemented because the specification lacks clarity. -4. Serialization and Description Routes: +3. Serialization and Description Routes: - `/serialization` - `/description` These routes are not implemented at this time. -5. Value, Path, and PATCH Routes: +4. Value, Path, and PATCH Routes: - All `/…/value$`, `/…/path$`, and `PATCH` routes are currently not implemented. -6. Operation Invocation Routes: The following routes are not implemented because operation invocation +5. Operation Invocation Routes: The following routes are not implemented because operation invocation is not yet supported by the `basyx-python-sdk`: - `POST /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/invoke` - `POST /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/invoke/$value` From f9b94e07c82910c80ffe7fc68a8ce4e70d926437 Mon Sep 17 00:00:00 2001 From: Sercan Sahin Date: Tue, 2 Dec 2025 17:23:02 +0100 Subject: [PATCH 2/4] use NonNegativeInteger per spec --- server/app/interfaces/base.py | 6 +++--- server/app/interfaces/repository.py | 24 ------------------------ 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/server/app/interfaces/base.py b/server/app/interfaces/base.py index c0f9248d..87ebc2b4 100644 --- a/server/app/interfaces/base.py +++ b/server/app/interfaces/base.py @@ -27,6 +27,7 @@ from basyx.aas.adapter.json import StrictStrippedAASFromJsonDecoder, StrictAASFromJsonDecoder, AASToJsonEncoder from basyx.aas.adapter.xml import xml_serialization, XMLConstructables, read_aas_xml_element from basyx.aas.model import AbstractObjectStore +from basyx.aas.model.datatypes import NonNegativeInteger from util.converters import base64url_decode # The following string aliases are constrained by the decorator functions defined in the string_constraints module, @@ -213,9 +214,8 @@ def _get_slice(cls, request: Request, iterator: Iterable[T]) -> Tuple[Iterator[T limit_str = request.args.get('limit', default="10") cursor_str = request.args.get('cursor', default="1") try: - limit, cursor = int(limit_str), int(cursor_str) - 1 # cursor is 1-indexed - if limit < 0 or cursor < 0: - raise ValueError + limit, cursor = (NonNegativeInteger(int(limit_str)), + NonNegativeInteger(int(cursor_str) - 1)) # cursor is 1-indexed except ValueError: raise BadRequest("Limit can not be negative, cursor must be positive!") start_index = cursor diff --git a/server/app/interfaces/repository.py b/server/app/interfaces/repository.py index 9db23659..713023d0 100644 --- a/server/app/interfaces/repository.py +++ b/server/app/interfaces/repository.py @@ -6,30 +6,6 @@ # SPDX-License-Identifier: MIT """ This module implements the "Specification of the Asset Administration Shell Part 2 Application Programming Interfaces". -However, several features and routes are currently not supported: - -1. Extent Parameter (`withBlobValue/withoutBlobValue`): - Not implemented due to the lack of support in JSON/XML serialization. - -2. Route `/shells/{aasIdentifier}/asset-information/thumbnail`: Not implemented because the specification lacks clarity. - -3. Serialization and Description Routes: - - `/serialization` - - `/description` - These routes are not implemented at this time. - -4. Value, Path, and PATCH Routes: - - All `/…/value$`, `/…/path$`, and `PATCH` routes are currently not implemented. - -5. Operation Invocation Routes: The following routes are not implemented because operation invocation - is not yet supported by the `basyx-python-sdk`: - - `POST /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/invoke` - - `POST /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/invoke/$value` - - `POST /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/invoke-async` - - `POST /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/invoke-async/$value` - - `GET /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/operation-status/{handleId}` - - `GET /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/operation-results/{handleId}` - - `GET /submodels/{submodelIdentifier}/submodel-elements/{idShortPath}/operation-results/{handleId}/$value` """ import io From f18b33881f7926a6b46c53b1d27043b7c7d48e5f Mon Sep 17 00:00:00 2001 From: Sercan Sahin <125310380+Frosty2500@users.noreply.github.com> Date: Wed, 10 Dec 2025 16:37:15 +0100 Subject: [PATCH 3/4] fix typo Co-authored-by: s-heppner --- sdk/basyx/aas/model/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/basyx/aas/model/base.py b/sdk/basyx/aas/model/base.py index 675a9f32..ca22d5bb 100644 --- a/sdk/basyx/aas/model/base.py +++ b/sdk/basyx/aas/model/base.py @@ -28,7 +28,7 @@ BlobType = bytes # The following string aliases are constrained by the decorator functions defined in the string_constraints module, -# wherever they are used for an instances attributes. +# wherever they are used for an instance's attributes. ContentType = str # any mimetype as in RFC2046 Identifier = str LabelType = str From 0bc68d69d8763e7a356e8fecf4677c31a59438ed Mon Sep 17 00:00:00 2001 From: Sercan Sahin Date: Wed, 10 Dec 2025 16:39:52 +0100 Subject: [PATCH 4/4] adapt import order --- server/app/interfaces/base.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/app/interfaces/base.py b/server/app/interfaces/base.py index 87ebc2b4..45d7f058 100644 --- a/server/app/interfaces/base.py +++ b/server/app/interfaces/base.py @@ -20,8 +20,6 @@ from werkzeug.exceptions import NotFound, BadRequest from werkzeug.routing import MapAdapter -from . import _string_constraints - from basyx.aas import model from basyx.aas.adapter._generic import XML_NS_MAP from basyx.aas.adapter.json import StrictStrippedAASFromJsonDecoder, StrictAASFromJsonDecoder, AASToJsonEncoder @@ -29,6 +27,7 @@ from basyx.aas.model import AbstractObjectStore from basyx.aas.model.datatypes import NonNegativeInteger from util.converters import base64url_decode +from . import _string_constraints # The following string aliases are constrained by the decorator functions defined in the string_constraints module, # wherever they are used for an instances attributes.