diff --git a/sdk/basyx/aas/model/base.py b/sdk/basyx/aas/model/base.py index 3edaac6e..c9c8c8fe 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 instance's 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 8868dd7f..1c1c77de 100644 --- a/server/app/interfaces/base.py +++ b/server/app/interfaces/base.py @@ -19,6 +19,7 @@ from basyx.aas.adapter._generic import XML_NS_MAP from basyx.aas.adapter.xml import XMLConstructables, read_aas_xml_element, xml_serialization from basyx.aas.model import AbstractObjectStore +from basyx.aas.model.datatypes import NonNegativeInteger from lxml import etree from werkzeug import Request, Response from werkzeug.exceptions import BadRequest, NotFound @@ -28,6 +29,15 @@ from app.adapter import ServerAASToJsonEncoder, ServerStrictAASFromJsonDecoder, ServerStrictStrippedAASFromJsonDecoder from app.model import AssetAdministrationShellDescriptor, AssetLink, SubmodelDescriptor from app.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. +CodeType = str +ShortIdType = str +LocatorType = str +TextType = str +SchemeType = str T = TypeVar("T") @@ -44,15 +54,16 @@ def __str__(self): return self.name.capitalize() +@_string_constraints.constrain_code_type("code") class Message: def __init__( self, - code: str, + 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 = ( @@ -203,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