Skip to content

Commit 4d81bd2

Browse files
committed
Use semver for document template metamodel version
1 parent 0393270 commit 4d81bd2

11 files changed

Lines changed: 108 additions & 44 deletions

File tree

packages/dsw-document-worker/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- Switch to use semver for document template metamodel versioning
13+
1014

1115
## [4.21.0]
1216

packages/dsw-document-worker/dsw/document_worker/consts.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
CMD_CHANNEL = 'doc_worker'
33
CMD_COMPONENT = 'doc_worker'
44
COMPONENT_NAME = 'Document Worker'
5-
CURRENT_METAMODEL = 16
5+
CURRENT_METAMODEL_MAJOR = 17
6+
CURRENT_METAMODEL_MINOR = 0
67
DEFAULT_ENCODING = 'utf-8'
78
EXIT_SUCCESS = 0
89
NULL_UUID = '00000000-0000-0000-0000-000000000000'

packages/dsw-document-worker/dsw/document_worker/model/context.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import dateutil.parser as dp
77

88
from ..consts import NULL_UUID
9+
from ..utils import check_metamodel_version
910
from .utils import unmarkdown
1011

1112
AnnotationsT = dict[str, str | list[str]]
@@ -2072,14 +2073,11 @@ def load(data: dict, **options):
20722073

20732074
class DocumentContext:
20742075
"""Document Context smart representation"""
2075-
METAMODEL_VERSION = 16
20762076

20772077
def __init__(self, *, ctx, **options):
2078-
self.metamodel_version = int(ctx.get('metamodelVersion', '0'))
2079-
if self.metamodel_version != self.METAMODEL_VERSION:
2080-
raise ValueError(f'Unsupported metamodel version: {self.metamodel_version} '
2081-
f'(expected: {self.METAMODEL_VERSION})')
2082-
2078+
check_metamodel_version(
2079+
metamodel_version=str(ctx.get('metamodelVersion', '0')),
2080+
)
20832081
self.config = ContextConfig.load(ctx['config'], **options)
20842082
self.km = KnowledgeModel.load(ctx['knowledgeModel'], **options)
20852083
self.questionnaire = Questionnaire.load(ctx['questionnaire'], **options)

packages/dsw-document-worker/dsw/document_worker/utils.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from .consts import CURRENT_METAMODEL_MAJOR, CURRENT_METAMODEL_MINOR
2+
3+
14
_BYTE_SIZES = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB"]
25

36

@@ -11,3 +14,18 @@ def byte_size_format(num: float):
1114
return f'{_round_size(num)} {unit}'
1215
num /= 1000.0
1316
return f'{_round_size(num)} YB'
17+
18+
19+
def check_metamodel_version(metamodel_version: str):
20+
version_parts = metamodel_version.split('.')
21+
try:
22+
major = int(version_parts[0]) if len(version_parts) > 0 else 0
23+
minor = int(version_parts[1]) if len(version_parts) > 1 else 0
24+
except ValueError as e:
25+
raise ValueError(f'Invalid metamodel version format: {metamodel_version}') from e
26+
if major != CURRENT_METAMODEL_MAJOR:
27+
raise ValueError(f'Unsupported metamodel version: {metamodel_version} '
28+
f'(expected major version {CURRENT_METAMODEL_MAJOR})')
29+
if minor < CURRENT_METAMODEL_MINOR:
30+
raise ValueError(f'Unsupported metamodel version: {metamodel_version} '
31+
f'(expected at least {CURRENT_METAMODEL_MINOR} minor version)')

packages/dsw-document-worker/dsw/document_worker/worker.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
from .build_info import BUILD_INFO
1717
from .config import DocumentWorkerConfig, TemplateConfig
1818
from .consts import DocumentState, COMPONENT_NAME, NULL_UUID, \
19-
CMD_COMPONENT, CMD_CHANNEL, PROG_NAME, CURRENT_METAMODEL
19+
CMD_COMPONENT, CMD_CHANNEL, PROG_NAME
2020
from .context import Context
2121
from .documents import DocumentFile, DocumentNameGiver
2222
from .exceptions import create_job_exception, JobException
2323
from .limits import LimitsEnforcer
2424
from .templates import TemplateRegistry, Template, Format
25-
from .utils import byte_size_format
25+
from .utils import byte_size_format, check_metamodel_version
2626

2727

2828
LOG = logging.getLogger(__name__)
@@ -161,12 +161,9 @@ def _enrich_context(self):
161161

162162
def check_compliance(self):
163163
SentryReporter.set_tags(phase='check')
164-
metamodel_version = int(self.doc_context.get('metamodelVersion', '0'))
165-
if metamodel_version != CURRENT_METAMODEL:
166-
LOG.error('Command with metamodel version %d is not supported '
167-
'by this worker (version %d)', metamodel_version, CURRENT_METAMODEL)
168-
raise RuntimeError(f'Unsupported metamodel version: {metamodel_version} '
169-
f'(expected {CURRENT_METAMODEL})')
164+
check_metamodel_version(
165+
metamodel_version=str(self.doc_context.get('metamodelVersion', '0')),
166+
)
170167

171168
@handle_job_step('Failed to build final document')
172169
def build_document(self):

packages/dsw-tdk/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- Switch to use semver for document template metamodel versioning
13+
1014

1115
## [4.21.0]
1216

packages/dsw-tdk/dsw/tdk/consts.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55

66
APP = 'dsw-tdk'
77
VERSION = '4.21.0'
8-
METAMODEL_VERSION = 16
8+
METAMODEL_VERSION_MAJOR = 17
9+
METAMODEL_VERSION_MINOR = 0
10+
METAMODEL_VERSION = f'{METAMODEL_VERSION_MAJOR}.{METAMODEL_VERSION_MINOR}'
911

1012
REGEX_SEMVER = re.compile(r'^[0-9]+\.[0-9]+\.[0-9]+$')
1113
REGEX_WIZARD_ID = re.compile(r'^[a-zA-Z0-9-_.]+$')

packages/dsw-tdk/dsw/tdk/core.py

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,30 +33,38 @@ def __init__(self, message: str, hint: str):
3333

3434

3535
METAMODEL_VERSION_SUPPORT = {
36-
1: (2, 5, 0),
37-
2: (2, 6, 0),
38-
3: (2, 12, 0),
39-
4: (3, 2, 0),
40-
5: (3, 5, 0),
41-
6: (3, 6, 0),
42-
7: (3, 7, 0),
43-
8: (3, 8, 0),
44-
9: (3, 10, 0),
45-
10: (3, 12, 0),
46-
11: (3, 20, 0),
47-
12: (4, 1, 0),
48-
13: (4, 3, 0),
49-
14: (4, 10, 0),
50-
15: (4, 12, 0),
51-
16: (4, 13, 0),
36+
'1.0': (2, 5, 0),
37+
'2.0': (2, 6, 0),
38+
'3.0': (2, 12, 0),
39+
'4.0': (3, 2, 0),
40+
'5.0': (3, 5, 0),
41+
'6.0': (3, 6, 0),
42+
'7.0': (3, 7, 0),
43+
'8.0': (3, 8, 0),
44+
'9.0': (3, 10, 0),
45+
'10.0': (3, 12, 0),
46+
'11.0': (3, 20, 0),
47+
'12.0': (4, 1, 0),
48+
'13.0': (4, 3, 0),
49+
'14.0': (4, 10, 0),
50+
'15.0': (4, 12, 0),
51+
'16.0': (4, 13, 0),
52+
'17.0': (4, 22, 0),
5253
}
5354

5455

5556
# pylint: disable=too-many-public-methods
5657
class TDKCore:
5758

5859
def _check_metamodel_version(self):
59-
mm_ver = self.safe_template.metamodel_version
60+
hint = 'Fix your metamodelVersion in template.json and/or visit docs'
61+
mm_ver = str(self.safe_template.metamodel_version)
62+
try:
63+
if '.' not in mm_ver:
64+
mm_ver = f'{mm_ver}.0'
65+
mm_major, mm_minor = map(int, mm_ver.split('.'))
66+
except ValueError as e:
67+
raise TDKProcessingError(f'Invalid metamodel version format: {mm_ver}', hint) from e
6068
api_version = self.remote_version.split('~', maxsplit=1)[0]
6169
if '-' in api_version:
6270
api_version = api_version.split('-', maxsplit=1)[0]
@@ -68,16 +76,17 @@ def _check_metamodel_version(self):
6876
parts = api_version.split('.')
6977
ver = (int(parts[0]), int(parts[1]), int(parts[2]))
7078
vtag = f'v{ver[0]}.{ver[1]}.{ver[2]}'
71-
hint = 'Fix your metamodelVersion in template.json and/or visit docs'
7279
if mm_ver not in METAMODEL_VERSION_SUPPORT:
7380
raise TDKProcessingError(f'Unknown metamodel version: {mm_ver}', hint)
74-
min_version = METAMODEL_VERSION_SUPPORT[mm_ver]
75-
if min_version > ver:
76-
raise TDKProcessingError(f'Unsupported metamodel version for API {vtag}', hint)
77-
if mm_ver + 1 in METAMODEL_VERSION_SUPPORT:
78-
max_version = METAMODEL_VERSION_SUPPORT[mm_ver + 1]
79-
if ver >= max_version:
80-
raise TDKProcessingError(f'Unsupported metamodel version for API {vtag}', hint)
81+
map_reverse = {f'v{v[0]}.{v[1]}.{v[2]}': k for k, v in METAMODEL_VERSION_SUPPORT.items()}
82+
if vtag not in map_reverse:
83+
raise TDKProcessingError(f'Unsupported API version: {vtag}', hint)
84+
api_mm_major, api_mm_minor = map_reverse[vtag].split('.')
85+
if mm_major != api_mm_major or mm_minor < api_mm_minor:
86+
raise TDKProcessingError(
87+
f'Unsupported metamodel version {mm_ver} for API version {api_version}',
88+
hint,
89+
)
8190

8291
def __init__(self, template: Template | None = None, project: TemplateProject | None = None,
8392
client: DSWAPIClient | None = None, logger: logging.Logger | None = None):

packages/dsw-tdk/dsw/tdk/model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ def __init__(self, *, template_id=None, organization_id=None, version=None, name
180180
self.description = description # type: str
181181
self.readme = readme # type: str
182182
self.license = template_license # type: str
183-
self.metamodel_version: int = metamodel_version or METAMODEL_VERSION
183+
self.metamodel_version: str = metamodel_version or METAMODEL_VERSION
184184
self.allowed_packages: list[PackageFilter] = []
185185
self.formats: list[Format] = []
186186
self.files: dict[str, TemplateFile] = {}

packages/dsw-tdk/dsw/tdk/validation.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,37 @@ def _validate_natural(field_name: str, value) -> List[ValidationError]:
9898
return []
9999

100100

101+
def _validate_metamodel_version(field_name: str, value) -> List[ValidationError]:
102+
if isinstance(value, int) and value > 0:
103+
return []
104+
if isinstance(value, str) and '.' in value:
105+
parts = value.split('.')
106+
if len(parts) != 2:
107+
return [ValidationError(
108+
field_name=field_name,
109+
message='It must be in format <MAJOR>.<MINOR>',
110+
)]
111+
try:
112+
major = int(parts[0])
113+
minor = int(parts[1])
114+
if major < 1 or minor < 0:
115+
return [ValidationError(
116+
field_name=field_name,
117+
message='It must be in format <MAJOR>.<MINOR> '
118+
'with MAJOR >= 1 and MINOR >= 0',
119+
)]
120+
except ValueError:
121+
return [ValidationError(
122+
field_name=field_name,
123+
message='It must be in format <MAJOR>.<MINOR> '
124+
'with MAJOR >= 1 and MINOR >= 0',
125+
)]
126+
return [ValidationError(
127+
field_name=field_name,
128+
message='It must be a positive integer or in format <MAJOR>.<MINOR>',
129+
)]
130+
131+
101132
def _validate_package_id(field_name: str, value: str) -> List[ValidationError]:
102133
res = []
103134
if value is None:
@@ -255,7 +286,7 @@ def _validate_formats(field_name: str, value: List[Format]) -> List[ValidationEr
255286
'description': [_validate_required, _validate_non_empty],
256287
'readme': [_validate_required, _validate_non_empty],
257288
'license': [_validate_required, _validate_non_empty],
258-
'metamodel_version': [_validate_natural],
289+
'metamodel_version': [_validate_metamodel_version, _validate_required],
259290
'allowed_packages': [_validate_package_filters],
260291
'formats': [_validate_required, _validate_formats],
261292
})

0 commit comments

Comments
 (0)