dsw-tdk 4.12.0__py2.py3-none-any.whl → 4.14.0__py2.py3-none-any.whl
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.
- dsw/tdk/__main__.py +1 -0
- dsw/tdk/api_client.py +58 -30
- dsw/tdk/build_info.py +4 -4
- dsw/tdk/cli.py +59 -44
- dsw/tdk/consts.py +5 -4
- dsw/tdk/core.py +128 -99
- dsw/tdk/model.py +77 -67
- dsw/tdk/templates/starter.j2 +1 -2
- dsw/tdk/utils.py +8 -9
- dsw/tdk/validation.py +72 -18
- {dsw_tdk-4.12.0.dist-info → dsw_tdk-4.14.0.dist-info}/METADATA +3 -3
- dsw_tdk-4.14.0.dist-info/RECORD +20 -0
- {dsw_tdk-4.12.0.dist-info → dsw_tdk-4.14.0.dist-info}/WHEEL +1 -1
- dsw_tdk-4.12.0.dist-info/RECORD +0 -20
- {dsw_tdk-4.12.0.dist-info → dsw_tdk-4.14.0.dist-info}/LICENSE +0 -0
- {dsw_tdk-4.12.0.dist-info → dsw_tdk-4.14.0.dist-info}/entry_points.txt +0 -0
- {dsw_tdk-4.12.0.dist-info → dsw_tdk-4.14.0.dist-info}/top_level.txt +0 -0
dsw/tdk/model.py
CHANGED
|
@@ -3,29 +3,30 @@ import json
|
|
|
3
3
|
import logging
|
|
4
4
|
import mimetypes
|
|
5
5
|
import pathlib
|
|
6
|
-
import pathspec # type: ignore
|
|
7
6
|
|
|
8
7
|
from collections import OrderedDict
|
|
9
|
-
from typing import
|
|
8
|
+
from typing import Any
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
import pathspec
|
|
11
|
+
|
|
12
|
+
from .consts import VERSION, DEFAULT_ENCODING, METAMODEL_VERSION, PathspecFactory
|
|
12
13
|
|
|
13
14
|
mimetypes.init()
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class TemplateFileType(enum.Enum):
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
ASSET = 'asset'
|
|
19
|
+
FILE = 'file'
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class PackageFilter:
|
|
22
23
|
|
|
23
|
-
def __init__(self, *, organization_id=None, km_id
|
|
24
|
-
max_version=None):
|
|
25
|
-
self.organization_id = organization_id
|
|
26
|
-
self.km_id = km_id
|
|
27
|
-
self.min_version = min_version
|
|
28
|
-
self.max_version = max_version
|
|
24
|
+
def __init__(self, *, organization_id: str | None = None, km_id: str | None = None,
|
|
25
|
+
min_version: str | None = None, max_version: str | None = None):
|
|
26
|
+
self.organization_id = organization_id
|
|
27
|
+
self.km_id = km_id
|
|
28
|
+
self.min_version = min_version
|
|
29
|
+
self.max_version = max_version
|
|
29
30
|
|
|
30
31
|
@classmethod
|
|
31
32
|
def load(cls, data):
|
|
@@ -47,9 +48,10 @@ class PackageFilter:
|
|
|
47
48
|
|
|
48
49
|
class Step:
|
|
49
50
|
|
|
50
|
-
def __init__(self, *, name
|
|
51
|
-
|
|
52
|
-
self.
|
|
51
|
+
def __init__(self, *, name: str | None = None,
|
|
52
|
+
options: dict[str, str] | None = None):
|
|
53
|
+
self.name = name
|
|
54
|
+
self.options = options or {}
|
|
53
55
|
|
|
54
56
|
@classmethod
|
|
55
57
|
def load(cls, data):
|
|
@@ -69,11 +71,12 @@ class Format:
|
|
|
69
71
|
|
|
70
72
|
DEFAULT_ICON = 'fas fa-file'
|
|
71
73
|
|
|
72
|
-
def __init__(self, *, uuid=None, name
|
|
73
|
-
|
|
74
|
-
self.
|
|
75
|
-
self.
|
|
76
|
-
self.
|
|
74
|
+
def __init__(self, *, uuid: str | None = None, name: str | None = None,
|
|
75
|
+
icon: str | None = None):
|
|
76
|
+
self.uuid = uuid
|
|
77
|
+
self.name = name
|
|
78
|
+
self.icon: str = icon or self.DEFAULT_ICON
|
|
79
|
+
self.steps: list[Step] = []
|
|
77
80
|
|
|
78
81
|
@classmethod
|
|
79
82
|
def load(cls, data):
|
|
@@ -100,11 +103,12 @@ class TDKConfig:
|
|
|
100
103
|
DEFAULT_README = 'README.md'
|
|
101
104
|
DEFAULT_FILES = ['*']
|
|
102
105
|
|
|
103
|
-
def __init__(self, *, version=None, readme_file
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
self.
|
|
106
|
+
def __init__(self, *, version: str | None = None, readme_file: str | None = None,
|
|
107
|
+
files: list[str] | None = None):
|
|
108
|
+
self.version: str = version or VERSION
|
|
109
|
+
readme_file_str: str = readme_file or self.DEFAULT_README
|
|
110
|
+
self.readme_file: pathlib.Path = pathlib.Path(readme_file_str)
|
|
111
|
+
self.files: list[str] = files or []
|
|
108
112
|
|
|
109
113
|
@classmethod
|
|
110
114
|
def load(cls, data):
|
|
@@ -127,19 +131,19 @@ class TemplateFile:
|
|
|
127
131
|
DEFAULT_CONTENT_TYPE = 'application/octet-stream'
|
|
128
132
|
TEMPLATE_EXTENSIONS = ('.j2', '.jinja', '.jinja2', '.jnj')
|
|
129
133
|
|
|
130
|
-
def __init__(self, *,
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
self.
|
|
134
|
-
self.
|
|
135
|
-
self.
|
|
136
|
-
self.
|
|
134
|
+
def __init__(self, *, filename: pathlib.Path,
|
|
135
|
+
remote_id: str | None = None, remote_type: TemplateFileType | None = None,
|
|
136
|
+
content_type: str | None = None, content: bytes = b''):
|
|
137
|
+
self.remote_id = remote_id
|
|
138
|
+
self.filename = filename
|
|
139
|
+
self.content = content
|
|
140
|
+
self.content_type: str = content_type or self.guess_type()
|
|
141
|
+
self.remote_type: TemplateFileType = remote_type or self.guess_tfile_type()
|
|
137
142
|
|
|
138
143
|
def guess_tfile_type(self):
|
|
139
|
-
return TemplateFileType.
|
|
144
|
+
return TemplateFileType.FILE if self.is_text else TemplateFileType.ASSET
|
|
140
145
|
|
|
141
146
|
def guess_type(self) -> str:
|
|
142
|
-
# TODO: add own map of file extensions
|
|
143
147
|
filename = self.filename.name
|
|
144
148
|
for ext in self.TEMPLATE_EXTENSIONS:
|
|
145
149
|
if filename.endswith(ext):
|
|
@@ -151,8 +155,7 @@ class TemplateFile:
|
|
|
151
155
|
|
|
152
156
|
@property
|
|
153
157
|
def is_text(self):
|
|
154
|
-
|
|
155
|
-
if getattr(self, 'remote_type', None) == TemplateFileType.file:
|
|
158
|
+
if getattr(self, 'remote_type', None) == TemplateFileType.FILE:
|
|
156
159
|
return True
|
|
157
160
|
return self.content_type.startswith('text')
|
|
158
161
|
|
|
@@ -163,6 +166,7 @@ class TemplateFile:
|
|
|
163
166
|
|
|
164
167
|
class Template:
|
|
165
168
|
|
|
169
|
+
# pylint: disable-next=too-many-arguments
|
|
166
170
|
def __init__(self, *, template_id=None, organization_id=None, version=None, name=None,
|
|
167
171
|
description=None, readme=None, template_license=None,
|
|
168
172
|
metamodel_version=None, tdk_config=None, loaded_json=None):
|
|
@@ -173,13 +177,13 @@ class Template:
|
|
|
173
177
|
self.description = description # type: str
|
|
174
178
|
self.readme = readme # type: str
|
|
175
179
|
self.license = template_license # type: str
|
|
176
|
-
self.metamodel_version = metamodel_version or METAMODEL_VERSION
|
|
177
|
-
self.allowed_packages
|
|
178
|
-
self.formats
|
|
179
|
-
self.files
|
|
180
|
-
self.extras
|
|
181
|
-
self.tdk_config = tdk_config or TDKConfig()
|
|
182
|
-
self.loaded_json = loaded_json or OrderedDict()
|
|
180
|
+
self.metamodel_version: int = metamodel_version or METAMODEL_VERSION
|
|
181
|
+
self.allowed_packages: list[PackageFilter] = []
|
|
182
|
+
self.formats: list[Format] = []
|
|
183
|
+
self.files: dict[str, TemplateFile] = {}
|
|
184
|
+
self.extras: list[TemplateFile] = []
|
|
185
|
+
self.tdk_config: TDKConfig = tdk_config or TDKConfig()
|
|
186
|
+
self.loaded_json: OrderedDict = loaded_json or OrderedDict()
|
|
183
187
|
|
|
184
188
|
@property
|
|
185
189
|
def id(self) -> str:
|
|
@@ -200,8 +204,8 @@ class Template:
|
|
|
200
204
|
org_id = data['organizationId']
|
|
201
205
|
tmp_id = data['templateId']
|
|
202
206
|
version = data['version']
|
|
203
|
-
except KeyError:
|
|
204
|
-
raise RuntimeError('Cannot retrieve template ID')
|
|
207
|
+
except KeyError as e:
|
|
208
|
+
raise RuntimeError('Cannot retrieve template ID') from e
|
|
205
209
|
template = Template(
|
|
206
210
|
template_id=tmp_id,
|
|
207
211
|
organization_id=org_id,
|
|
@@ -244,7 +248,7 @@ class Template:
|
|
|
244
248
|
self.loaded_json['_tdk'] = self.tdk_config.serialize()
|
|
245
249
|
return self.loaded_json
|
|
246
250
|
|
|
247
|
-
def serialize_remote(self) ->
|
|
251
|
+
def serialize_remote(self) -> dict[str, Any]:
|
|
248
252
|
return {
|
|
249
253
|
'id': self.id,
|
|
250
254
|
'templateId': self.template_id,
|
|
@@ -260,7 +264,7 @@ class Template:
|
|
|
260
264
|
'phase': 'DraftDocumentTemplatePhase',
|
|
261
265
|
}
|
|
262
266
|
|
|
263
|
-
def serialize_for_update(self) ->
|
|
267
|
+
def serialize_for_update(self) -> dict[str, Any]:
|
|
264
268
|
return {
|
|
265
269
|
'templateId': self.template_id,
|
|
266
270
|
'version': self.version,
|
|
@@ -274,7 +278,7 @@ class Template:
|
|
|
274
278
|
'phase': 'DraftDocumentTemplatePhase',
|
|
275
279
|
}
|
|
276
280
|
|
|
277
|
-
def serialize_for_create(self, based_on:
|
|
281
|
+
def serialize_for_create(self, based_on: str | None = None) -> dict[str, Any]:
|
|
278
282
|
return {
|
|
279
283
|
'basedOn': based_on,
|
|
280
284
|
'name': self.name,
|
|
@@ -282,7 +286,7 @@ class Template:
|
|
|
282
286
|
'version': self.version,
|
|
283
287
|
}
|
|
284
288
|
|
|
285
|
-
def serialize_local_new(self) ->
|
|
289
|
+
def serialize_local_new(self) -> dict[str, Any]:
|
|
286
290
|
return {
|
|
287
291
|
'templateId': self.template_id,
|
|
288
292
|
'organizationId': self.organization_id,
|
|
@@ -297,23 +301,24 @@ class Template:
|
|
|
297
301
|
}
|
|
298
302
|
|
|
299
303
|
|
|
300
|
-
def _to_ordered_dict(tuples:
|
|
304
|
+
def _to_ordered_dict(tuples: list[tuple[str, Any]]) -> OrderedDict:
|
|
301
305
|
return OrderedDict(tuples)
|
|
302
306
|
|
|
303
307
|
|
|
304
308
|
class TemplateProject:
|
|
305
309
|
|
|
306
310
|
TEMPLATE_FILE = 'template.json'
|
|
307
|
-
DEFAULT_PATTERNS = ['
|
|
311
|
+
DEFAULT_PATTERNS = ['!**/.*', '!**/.*/', '!**/~*', '!**/~*/',
|
|
312
|
+
'!template.json', '!template.zip']
|
|
308
313
|
|
|
309
314
|
json_decoder = json.JSONDecoder(object_pairs_hook=_to_ordered_dict)
|
|
310
315
|
|
|
311
|
-
def __init__(self, template_dir, logger):
|
|
312
|
-
self.template_dir = pathlib.Path(template_dir)
|
|
316
|
+
def __init__(self, template_dir: pathlib.Path, logger: logging.Logger):
|
|
317
|
+
self.template_dir = pathlib.Path(template_dir)
|
|
313
318
|
self.descriptor_path = self.template_dir / self.TEMPLATE_FILE
|
|
314
|
-
self.template
|
|
315
|
-
self.used_readme
|
|
316
|
-
self._logger = logger
|
|
319
|
+
self.template: Template | None = None
|
|
320
|
+
self.used_readme: pathlib.Path | None = None
|
|
321
|
+
self._logger = logger
|
|
317
322
|
|
|
318
323
|
@property
|
|
319
324
|
def logger(self) -> logging.Logger:
|
|
@@ -331,8 +336,8 @@ class TemplateProject:
|
|
|
331
336
|
try:
|
|
332
337
|
content = self.descriptor_path.read_text(encoding=DEFAULT_ENCODING)
|
|
333
338
|
self.template = Template.load_local(self.json_decoder.decode(content))
|
|
334
|
-
except Exception:
|
|
335
|
-
raise RuntimeError(f'Unable to load template using {self.descriptor_path}.')
|
|
339
|
+
except Exception as e:
|
|
340
|
+
raise RuntimeError(f'Unable to load template using {self.descriptor_path}.') from e
|
|
336
341
|
|
|
337
342
|
def load_readme(self):
|
|
338
343
|
readme = self.safe_template.tdk_config.readme_file
|
|
@@ -341,7 +346,7 @@ class TemplateProject:
|
|
|
341
346
|
self.used_readme = self.template_dir / readme
|
|
342
347
|
self.safe_template.readme = self.used_readme.read_text(encoding=DEFAULT_ENCODING)
|
|
343
348
|
except Exception as e:
|
|
344
|
-
raise RuntimeWarning(f'README file "{readme}" cannot be loaded: {e}')
|
|
349
|
+
raise RuntimeWarning(f'README file "{readme}" cannot be loaded: {e}') from e
|
|
345
350
|
|
|
346
351
|
def load_file(self, filepath: pathlib.Path) -> TemplateFile:
|
|
347
352
|
try:
|
|
@@ -353,7 +358,7 @@ class TemplateProject:
|
|
|
353
358
|
self.safe_template.files[filepath.as_posix()] = tfile
|
|
354
359
|
return tfile
|
|
355
360
|
except Exception as e:
|
|
356
|
-
raise RuntimeWarning(f'Failed to load template file {filepath}: {e}')
|
|
361
|
+
raise RuntimeWarning(f'Failed to load template file {filepath}: {e}') from e
|
|
357
362
|
|
|
358
363
|
def load_files(self):
|
|
359
364
|
self.safe_template.files.clear()
|
|
@@ -362,22 +367,24 @@ class TemplateProject:
|
|
|
362
367
|
|
|
363
368
|
@property
|
|
364
369
|
def files_pathspec(self) -> pathspec.PathSpec:
|
|
365
|
-
# TODO: make this more efficient (reload only when tdk_config changes, otherwise cache)
|
|
366
370
|
patterns = self.safe_template.tdk_config.files + self.DEFAULT_PATTERNS
|
|
367
|
-
return pathspec.PathSpec.from_lines(
|
|
371
|
+
return pathspec.PathSpec.from_lines(PathspecFactory, patterns)
|
|
368
372
|
|
|
369
|
-
def list_files(self) ->
|
|
370
|
-
files = (pathlib.Path(p)
|
|
373
|
+
def list_files(self) -> list[pathlib.Path]:
|
|
374
|
+
files = (pathlib.Path(p)
|
|
375
|
+
for p in self.files_pathspec.match_tree_files(str(self.template_dir)))
|
|
371
376
|
if self.used_readme is not None:
|
|
372
377
|
return list(p for p in files if p != self.used_readme.relative_to(self.template_dir))
|
|
373
378
|
return list(files)
|
|
374
379
|
|
|
375
|
-
def _relative_paths_eq(self, filepath1:
|
|
380
|
+
def _relative_paths_eq(self, filepath1: pathlib.Path | None,
|
|
381
|
+
filepath2: pathlib.Path | None) -> bool:
|
|
376
382
|
if filepath1 is None or filepath2 is None:
|
|
377
383
|
return False
|
|
378
384
|
return filepath1.relative_to(self.template_dir) == filepath2.relative_to(self.template_dir)
|
|
379
385
|
|
|
380
|
-
def is_template_file(self, filepath: pathlib.Path, include_descriptor: bool = False,
|
|
386
|
+
def is_template_file(self, filepath: pathlib.Path, include_descriptor: bool = False,
|
|
387
|
+
include_readme: bool = False):
|
|
381
388
|
if include_readme and self._relative_paths_eq(filepath, self.used_readme):
|
|
382
389
|
return True
|
|
383
390
|
if include_descriptor and self._relative_paths_eq(filepath, self.descriptor_path):
|
|
@@ -400,7 +407,7 @@ class TemplateProject:
|
|
|
400
407
|
filename = tfile.filename.as_posix()
|
|
401
408
|
self.safe_template.files[filename] = tfile
|
|
402
409
|
|
|
403
|
-
def get_template_file(self, filepath: pathlib.Path) ->
|
|
410
|
+
def get_template_file(self, filepath: pathlib.Path) -> TemplateFile | None:
|
|
404
411
|
if filepath.is_absolute():
|
|
405
412
|
filepath = filepath.relative_to(self.template_dir)
|
|
406
413
|
return self.safe_template.files.get(filepath.as_posix(), None)
|
|
@@ -419,7 +426,10 @@ class TemplateProject:
|
|
|
419
426
|
def store_descriptor(self, force: bool):
|
|
420
427
|
self._write_file(
|
|
421
428
|
filepath=self.descriptor_path,
|
|
422
|
-
contents=json.dumps(
|
|
429
|
+
contents=json.dumps(
|
|
430
|
+
obj=self.safe_template.serialize_local(),
|
|
431
|
+
indent=4
|
|
432
|
+
).encode(encoding=DEFAULT_ENCODING),
|
|
423
433
|
force=force,
|
|
424
434
|
)
|
|
425
435
|
|
dsw/tdk/templates/starter.j2
CHANGED
dsw/tdk/utils.py
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import jinja2 # type: ignore
|
|
2
1
|
import pathlib
|
|
3
2
|
import uuid
|
|
4
3
|
|
|
5
|
-
|
|
4
|
+
import jinja2
|
|
6
5
|
|
|
7
6
|
from .consts import DEFAULT_ENCODING, DEFAULT_README
|
|
8
7
|
from .model import Template, TemplateFile, Format, Step, PackageFilter
|
|
@@ -20,10 +19,10 @@ j2_env = jinja2.Environment(
|
|
|
20
19
|
|
|
21
20
|
class UUIDGen:
|
|
22
21
|
|
|
23
|
-
_uuids
|
|
22
|
+
_uuids: set[uuid.UUID] = set()
|
|
24
23
|
|
|
25
24
|
@classmethod
|
|
26
|
-
def used(cls) ->
|
|
25
|
+
def used(cls) -> set[uuid.UUID]:
|
|
27
26
|
return cls._uuids
|
|
28
27
|
|
|
29
28
|
@classmethod
|
|
@@ -91,10 +90,10 @@ class TemplateBuilder:
|
|
|
91
90
|
|
|
92
91
|
def __init__(self):
|
|
93
92
|
self.template = Template()
|
|
94
|
-
self._formats
|
|
93
|
+
self._formats: list[FormatSpec] = []
|
|
95
94
|
|
|
96
95
|
@property
|
|
97
|
-
def formats(self) ->
|
|
96
|
+
def formats(self) -> list[FormatSpec]:
|
|
98
97
|
return self._formats
|
|
99
98
|
|
|
100
99
|
def _validate_field(self, field_name: str):
|
|
@@ -177,13 +176,13 @@ class TemplateBuilder:
|
|
|
177
176
|
license_file = j2_env.get_template('LICENSE.j2').render(template=self.template)
|
|
178
177
|
self.template.tdk_config.files.append('LICENSE')
|
|
179
178
|
self.template.files['LICENSE'] = TemplateFile(
|
|
180
|
-
filename='LICENSE',
|
|
179
|
+
filename=pathlib.Path('LICENSE'),
|
|
181
180
|
content_type='text/plain',
|
|
182
181
|
content=license_file.encode(encoding=DEFAULT_ENCODING),
|
|
183
182
|
)
|
|
184
183
|
|
|
185
184
|
self.template.files['.env'] = TemplateFile(
|
|
186
|
-
filename='.env',
|
|
185
|
+
filename=pathlib.Path('.env'),
|
|
187
186
|
content_type='text/plain',
|
|
188
187
|
content=create_dot_env().encode(encoding=DEFAULT_ENCODING),
|
|
189
188
|
)
|
|
@@ -191,7 +190,7 @@ class TemplateBuilder:
|
|
|
191
190
|
return self.template
|
|
192
191
|
|
|
193
192
|
|
|
194
|
-
def create_dot_env(api_url:
|
|
193
|
+
def create_dot_env(api_url: str | None = None, api_key: str | None = None) -> str:
|
|
195
194
|
return j2_env.get_template('env.j2').render(api_url=api_url, api_key=api_key)
|
|
196
195
|
|
|
197
196
|
|
dsw/tdk/validation.py
CHANGED
|
@@ -15,55 +15,82 @@ class ValidationError(BaseException):
|
|
|
15
15
|
|
|
16
16
|
def _validate_required(field_name: str, value) -> List[ValidationError]:
|
|
17
17
|
if value is None:
|
|
18
|
-
return [ValidationError(
|
|
18
|
+
return [ValidationError(
|
|
19
|
+
field_name=field_name,
|
|
20
|
+
message='Missing but it is required',
|
|
21
|
+
)]
|
|
19
22
|
return []
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
def _validate_non_empty(field_name: str, value) -> List[ValidationError]:
|
|
23
26
|
if value is not None and len(value.strip()) == 0:
|
|
24
|
-
return [ValidationError(
|
|
27
|
+
return [ValidationError(
|
|
28
|
+
field_name=field_name,
|
|
29
|
+
message='Cannot be empty or only-whitespace',
|
|
30
|
+
)]
|
|
25
31
|
return []
|
|
26
32
|
|
|
27
33
|
|
|
28
34
|
def _validate_content_type(field_name: str, value) -> List[ValidationError]:
|
|
29
35
|
if value is not None and re.match(REGEX_MIME_TYPE, value) is None:
|
|
30
|
-
return [ValidationError(
|
|
36
|
+
return [ValidationError(
|
|
37
|
+
field_name=field_name,
|
|
38
|
+
message='Content type should be valid IANA media type',
|
|
39
|
+
)]
|
|
31
40
|
return []
|
|
32
41
|
|
|
33
42
|
|
|
34
43
|
def _validate_extension(field_name: str, value) -> List[ValidationError]:
|
|
35
44
|
if value is not None and re.match(REGEX_ORGANIZATION_ID, value) is None:
|
|
36
|
-
return [ValidationError(
|
|
45
|
+
return [ValidationError(
|
|
46
|
+
field_name=field_name,
|
|
47
|
+
message='File extension should contain only letters, numbers and dots (inside-only)',
|
|
48
|
+
)]
|
|
37
49
|
return []
|
|
38
50
|
|
|
39
51
|
|
|
40
52
|
def _validate_organization_id(field_name: str, value) -> List[ValidationError]:
|
|
41
53
|
if value is not None and re.match(REGEX_ORGANIZATION_ID, value) is None:
|
|
42
|
-
return [ValidationError(
|
|
54
|
+
return [ValidationError(
|
|
55
|
+
field_name=field_name,
|
|
56
|
+
message='Organization ID may contain only letters, numbers, and period (inside-only)',
|
|
57
|
+
)]
|
|
43
58
|
return []
|
|
44
59
|
|
|
45
60
|
|
|
46
61
|
def _validate_template_id(field_name: str, value) -> List[ValidationError]:
|
|
47
62
|
if value is not None and re.match(REGEX_TEMPLATE_ID, value) is None:
|
|
48
|
-
return [ValidationError(
|
|
63
|
+
return [ValidationError(
|
|
64
|
+
field_name=field_name,
|
|
65
|
+
message='Template ID may contain only letters, numbers, and dash (inside-only)',
|
|
66
|
+
)]
|
|
49
67
|
return []
|
|
50
68
|
|
|
51
69
|
|
|
52
70
|
def _validate_km_id(field_name: str, value) -> List[ValidationError]:
|
|
53
71
|
if value is not None and re.match(REGEX_KM_ID, value) is None:
|
|
54
|
-
return [ValidationError(
|
|
72
|
+
return [ValidationError(
|
|
73
|
+
field_name=field_name,
|
|
74
|
+
message='KM ID may contain only letters, numbers, and dash (inside-only)',
|
|
75
|
+
)]
|
|
55
76
|
return []
|
|
56
77
|
|
|
57
78
|
|
|
58
79
|
def _validate_version(field_name: str, value) -> List[ValidationError]:
|
|
59
80
|
if value is not None and re.match(REGEX_SEMVER, value) is None:
|
|
60
|
-
return [ValidationError(
|
|
81
|
+
return [ValidationError(
|
|
82
|
+
field_name=field_name,
|
|
83
|
+
message='Version must be in semver format <NUM>.<NUM>.<NUM>',
|
|
84
|
+
)]
|
|
61
85
|
return []
|
|
62
86
|
|
|
63
87
|
|
|
64
88
|
def _validate_natural(field_name: str, value) -> List[ValidationError]:
|
|
65
89
|
if value is not None and (not isinstance(value, int) or value < 1):
|
|
66
|
-
return [ValidationError(
|
|
90
|
+
return [ValidationError(
|
|
91
|
+
field_name=field_name,
|
|
92
|
+
message='It must be positive integer',
|
|
93
|
+
)]
|
|
67
94
|
return []
|
|
68
95
|
|
|
69
96
|
|
|
@@ -72,16 +99,31 @@ def _validate_package_id(field_name: str, value: str) -> List[ValidationError]:
|
|
|
72
99
|
if value is None:
|
|
73
100
|
return res
|
|
74
101
|
if not isinstance(value, str):
|
|
75
|
-
return [ValidationError(
|
|
102
|
+
return [ValidationError(
|
|
103
|
+
field_name=field_name,
|
|
104
|
+
message='Package ID is not a string',
|
|
105
|
+
)]
|
|
76
106
|
parts = value.split(':')
|
|
77
107
|
if len(parts) != 3:
|
|
78
|
-
res.append(ValidationError(
|
|
108
|
+
res.append(ValidationError(
|
|
109
|
+
field_name=field_name,
|
|
110
|
+
message='Package ID is not valid (only {len(parts)} parts)',
|
|
111
|
+
))
|
|
79
112
|
if re.match(REGEX_ORGANIZATION_ID, parts[0]) is None:
|
|
80
|
-
res.append(ValidationError(
|
|
113
|
+
res.append(ValidationError(
|
|
114
|
+
field_name=field_name,
|
|
115
|
+
message='Package ID contains invalid organization id',
|
|
116
|
+
))
|
|
81
117
|
if re.match(REGEX_KM_ID, parts[1]) is None:
|
|
82
|
-
res.append(ValidationError(
|
|
118
|
+
res.append(ValidationError(
|
|
119
|
+
field_name=field_name,
|
|
120
|
+
message='Package ID contains invalid KM id',
|
|
121
|
+
))
|
|
83
122
|
if re.match(REGEX_SEMVER, parts[2]) is None:
|
|
84
|
-
res.append(ValidationError(
|
|
123
|
+
res.append(ValidationError(
|
|
124
|
+
field_name=field_name,
|
|
125
|
+
message='Package ID contains invalid version',
|
|
126
|
+
))
|
|
85
127
|
return res
|
|
86
128
|
|
|
87
129
|
|
|
@@ -91,11 +133,20 @@ def _validate_jinja_options(field_name: str, value: Dict[str, str]) -> List[Vali
|
|
|
91
133
|
return res
|
|
92
134
|
for k in ('template', 'content-type', 'extension'):
|
|
93
135
|
if k not in value.keys():
|
|
94
|
-
res.append(ValidationError(
|
|
136
|
+
res.append(ValidationError(
|
|
137
|
+
field_name=field_name,
|
|
138
|
+
message='Jinja option cannot be left out',
|
|
139
|
+
))
|
|
95
140
|
elif value[k] is None or not isinstance(value[k], str) or len(value[k]) == 0:
|
|
96
|
-
res.append(ValidationError(
|
|
141
|
+
res.append(ValidationError(
|
|
142
|
+
field_name=field_name,
|
|
143
|
+
message='Jinja option cannot be empty',
|
|
144
|
+
))
|
|
97
145
|
if 'content-type' in value.keys():
|
|
98
|
-
res.extend(_validate_content_type(
|
|
146
|
+
res.extend(_validate_content_type(
|
|
147
|
+
field_name=f'{field_name}.content-type',
|
|
148
|
+
value=value['content-type'],
|
|
149
|
+
))
|
|
99
150
|
return res
|
|
100
151
|
|
|
101
152
|
|
|
@@ -129,7 +180,10 @@ class GenericValidator:
|
|
|
129
180
|
if field_name.startswith('__'):
|
|
130
181
|
continue
|
|
131
182
|
for validator in validators:
|
|
132
|
-
result.extend(validator(
|
|
183
|
+
result.extend(validator(
|
|
184
|
+
field_name=f'{field_name_prefix}{field_name}',
|
|
185
|
+
value=getattr(entity, field_name),
|
|
186
|
+
))
|
|
133
187
|
if '__all' in self.rules.keys():
|
|
134
188
|
result.extend(self.rules['__all'](field_name_prefix, entity))
|
|
135
189
|
return result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dsw-tdk
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.14.0
|
|
4
4
|
Summary: Data Stewardship Wizard Template Development Toolkit
|
|
5
5
|
Author-email: Marek Suchánek <marek.suchanek@ds-wizard.org>
|
|
6
6
|
License: Apache License 2.0
|
|
@@ -12,12 +12,12 @@ Classifier: Framework :: AsyncIO
|
|
|
12
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
13
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
14
|
Classifier: Programming Language :: Python
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
19
19
|
Classifier: Topic :: Utilities
|
|
20
|
-
Requires-Python: <4,>=3.
|
|
20
|
+
Requires-Python: <4,>=3.10
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
23
|
Requires-Dist: aiohttp
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
dsw/tdk/__init__.py,sha256=zJeTybb0SqiNdGLkANjRa36hku4axi17mZL2dM3tHto,288
|
|
2
|
+
dsw/tdk/__main__.py,sha256=cCzxo6f7raF50kQE-nM_segNjCNe723ZrxOBB6f3qwc,84
|
|
3
|
+
dsw/tdk/api_client.py,sha256=1WpUSgi8BfNrq3rUHWSfcZnmNGydID1mzkWBp5TnNmc,14727
|
|
4
|
+
dsw/tdk/build_info.py,sha256=xBZ1XqKVizhNtN7VHteBe1AGMyK4GwZrZQtrNx-EZz8,381
|
|
5
|
+
dsw/tdk/cli.py,sha256=7OYoGvceSd2TuyeRXFv7ikylJNKFk3Ua8t9l_Vas12E,22012
|
|
6
|
+
dsw/tdk/consts.py,sha256=WwGlNfUF50WmQZQwyRDRPufOAXwwABjJuF2lkTU9VMM,619
|
|
7
|
+
dsw/tdk/core.py,sha256=LvpB8J_AmAe-ljEIYjO3oP45EDAgjzKGEnZkkDqNiho,26448
|
|
8
|
+
dsw/tdk/model.py,sha256=5_qSOEdEeikskvoThClPQU2szS3pDw8-R2kDQVdBf7U,17151
|
|
9
|
+
dsw/tdk/utils.py,sha256=ICZpgxkDRWg9lq1gnHyQCEISFMtmGtu8Hl5yEclwn14,5685
|
|
10
|
+
dsw/tdk/validation.py,sha256=6GTyBpSvrvj73A2RkFJ-NL7SCjXFI4JnPneAEumR-ww,8847
|
|
11
|
+
dsw/tdk/templates/LICENSE.j2,sha256=6WzK06169rxZ4V_boYgejcZkw-1Up_WoU18iI3Gbkfs,60
|
|
12
|
+
dsw/tdk/templates/README.md.j2,sha256=FzUABeMM8To0oT48Kytoox64uAZ8F7FSAAXgpyKzqdU,247
|
|
13
|
+
dsw/tdk/templates/env.j2,sha256=7IJ6HAIY1bXKLake4c72DSEUcQtAquO5wlSgH8vg_8s,97
|
|
14
|
+
dsw/tdk/templates/starter.j2,sha256=JKJIkiXsmBO4P6pKXM4IvSz3JgJhqCnaZI1NVfPPnXg,246
|
|
15
|
+
dsw_tdk-4.14.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
16
|
+
dsw_tdk-4.14.0.dist-info/METADATA,sha256=1CXbl6itGPeaQ2wm-GTEzEg52J6J602oJG0pi_rnBIk,6022
|
|
17
|
+
dsw_tdk-4.14.0.dist-info/WHEEL,sha256=M1ikteR9eetPNvm1LyQ3rpXxNYuGd90oakQO1a-ohSk,109
|
|
18
|
+
dsw_tdk-4.14.0.dist-info/entry_points.txt,sha256=lwD5ZzRCbTFSjP1-SkhYsaJe8sEXOWWgMAMUhw0v2Hk,41
|
|
19
|
+
dsw_tdk-4.14.0.dist-info/top_level.txt,sha256=7SfbsHFoJ_vlAgG6C-xzETETwYO71dBrGnod8uMFnjw,4
|
|
20
|
+
dsw_tdk-4.14.0.dist-info/RECORD,,
|
dsw_tdk-4.12.0.dist-info/RECORD
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
dsw/tdk/__init__.py,sha256=zJeTybb0SqiNdGLkANjRa36hku4axi17mZL2dM3tHto,288
|
|
2
|
-
dsw/tdk/__main__.py,sha256=IkqeOatxravhcQCdk4l0BkbaMILkgVopB9phYCIR1uo,38
|
|
3
|
-
dsw/tdk/api_client.py,sha256=N3-PoO__kPtqFmR2QY-OG-9OkjYZwuXg8JxcBgpI8vQ,14222
|
|
4
|
-
dsw/tdk/build_info.py,sha256=tppWzn16SBK4k6X9Kb1tcx2_lK7t2IXi0_N9QmbdW1s,381
|
|
5
|
-
dsw/tdk/cli.py,sha256=_DZRSAo04wajiWVTod6lPdh477Rdxf7zBomySPxiL1c,21676
|
|
6
|
-
dsw/tdk/consts.py,sha256=40HjoDQ4bX5X4G9JYhZ7jPbWI6Xj6NBCeXMTWJPENmQ,635
|
|
7
|
-
dsw/tdk/core.py,sha256=Tyv2KXgBlm9mbJqFMaeyn6-07JE1nPHbKWTIKAvu2XY,25970
|
|
8
|
-
dsw/tdk/model.py,sha256=B9_cCmg_Z2IHabMMychKFV4dh4SFPVEentuPcb5fzlY,17235
|
|
9
|
-
dsw/tdk/utils.py,sha256=gW_-1qj7osHgKhTjjMGeVL2pxoBDQuq7VjRE6OmEMCw,5734
|
|
10
|
-
dsw/tdk/validation.py,sha256=hr04MRVqwBIBeKoX6v53Ix1S-vjvIQYM5dwI0SnMRsc,7827
|
|
11
|
-
dsw/tdk/templates/LICENSE.j2,sha256=6WzK06169rxZ4V_boYgejcZkw-1Up_WoU18iI3Gbkfs,60
|
|
12
|
-
dsw/tdk/templates/README.md.j2,sha256=FzUABeMM8To0oT48Kytoox64uAZ8F7FSAAXgpyKzqdU,247
|
|
13
|
-
dsw/tdk/templates/env.j2,sha256=7IJ6HAIY1bXKLake4c72DSEUcQtAquO5wlSgH8vg_8s,97
|
|
14
|
-
dsw/tdk/templates/starter.j2,sha256=XjZy3T9i8aWFlq4clXL6Q4JNh455crGelR9AoHisTbw,296
|
|
15
|
-
dsw_tdk-4.12.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
16
|
-
dsw_tdk-4.12.0.dist-info/METADATA,sha256=ldEae9hLRn_sVciIFiUTULYaCeqpoSaqsp6ElbqvoCQ,6020
|
|
17
|
-
dsw_tdk-4.12.0.dist-info/WHEEL,sha256=OpXWERl2xLPRHTvd2ZXo_iluPEQd8uSbYkJ53NAER_Y,109
|
|
18
|
-
dsw_tdk-4.12.0.dist-info/entry_points.txt,sha256=lwD5ZzRCbTFSjP1-SkhYsaJe8sEXOWWgMAMUhw0v2Hk,41
|
|
19
|
-
dsw_tdk-4.12.0.dist-info/top_level.txt,sha256=7SfbsHFoJ_vlAgG6C-xzETETwYO71dBrGnod8uMFnjw,4
|
|
20
|
-
dsw_tdk-4.12.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|