dsw-tdk 4.13.0__tar.gz → 4.14.0__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.
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/CHANGELOG.md +8 -0
- {dsw_tdk-4.13.0/dsw_tdk.egg-info → dsw_tdk-4.14.0}/PKG-INFO +3 -3
- dsw_tdk-4.14.0/dsw/tdk/__main__.py +4 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw/tdk/api_client.py +58 -30
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw/tdk/build_info.py +4 -4
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw/tdk/cli.py +59 -44
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw/tdk/consts.py +4 -3
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw/tdk/core.py +127 -99
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw/tdk/model.py +77 -67
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw/tdk/templates/starter.j2 +1 -2
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw/tdk/utils.py +8 -9
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw/tdk/validation.py +72 -18
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0/dsw_tdk.egg-info}/PKG-INFO +3 -3
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/pyproject.toml +3 -3
- dsw_tdk-4.13.0/dsw/tdk/__main__.py +0 -3
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/LICENSE +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/MANIFEST.in +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/README.md +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw/tdk/__init__.py +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw/tdk/templates/LICENSE.j2 +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw/tdk/templates/README.md.j2 +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw/tdk/templates/env.j2 +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw_tdk.egg-info/SOURCES.txt +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw_tdk.egg-info/dependency_links.txt +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw_tdk.egg-info/entry_points.txt +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw_tdk.egg-info/not-zip-safe +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw_tdk.egg-info/requires.txt +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/dsw_tdk.egg-info/top_level.txt +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/setup.cfg +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/setup.py +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/tests/test_basic.py +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/tests/test_cmd_dot-env.py +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/tests/test_cmd_get.py +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/tests/test_cmd_list.py +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/tests/test_cmd_new.py +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/tests/test_cmd_package.py +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/tests/test_cmd_put.py +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/tests/test_cmd_unpackage.py +0 -0
- {dsw_tdk-4.13.0 → dsw_tdk-4.14.0}/tests/test_cmd_verify.py +0 -0
|
@@ -8,6 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
## [4.14.0]
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Improved hidden file handling
|
|
16
|
+
- Updated initial Jinja file for new projects
|
|
17
|
+
|
|
11
18
|
## [4.13.0]
|
|
12
19
|
|
|
13
20
|
### Changed
|
|
@@ -509,3 +516,4 @@ Initial DSW Template Development Kit (versioned as part of the [DSW platform](ht
|
|
|
509
516
|
[4.11.0]: /../../tree/v4.11.0
|
|
510
517
|
[4.12.0]: /../../tree/v4.12.0
|
|
511
518
|
[4.13.0]: /../../tree/v4.13.0
|
|
519
|
+
[4.14.0]: /../../tree/v4.14.0
|
|
@@ -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
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import aiohttp
|
|
2
|
-
import aiohttp.client_exceptions
|
|
3
1
|
import functools
|
|
4
2
|
import pathlib
|
|
5
3
|
import urllib.parse
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
import aiohttp
|
|
6
|
+
import aiohttp.client_exceptions
|
|
8
7
|
|
|
9
8
|
from .consts import DEFAULT_ENCODING, APP, VERSION
|
|
10
9
|
from .model import Template, TemplateFile, TemplateFileType
|
|
@@ -36,30 +35,31 @@ def handle_client_errors(func):
|
|
|
36
35
|
raise DSWCommunicationError(
|
|
37
36
|
reason='Unexpected response type',
|
|
38
37
|
message=e.message
|
|
39
|
-
)
|
|
38
|
+
) from e
|
|
40
39
|
except aiohttp.client_exceptions.ClientResponseError as e:
|
|
41
40
|
raise DSWCommunicationError(
|
|
42
41
|
reason='Error response status',
|
|
43
42
|
message=f'Server responded with error HTTP status {e.status}: {e.message}'
|
|
44
|
-
)
|
|
43
|
+
) from e
|
|
45
44
|
except aiohttp.client_exceptions.InvalidURL as e:
|
|
46
45
|
raise DSWCommunicationError(
|
|
47
46
|
reason='Invalid URL',
|
|
48
47
|
message=f'Provided API URL seems invalid: {e.url}'
|
|
49
|
-
)
|
|
48
|
+
) from e
|
|
50
49
|
except aiohttp.client_exceptions.ClientConnectorError as e:
|
|
51
50
|
raise DSWCommunicationError(
|
|
52
51
|
reason='Server unreachable',
|
|
53
52
|
message=f'Desired server is not reachable (errno {e.os_error.errno})'
|
|
54
|
-
)
|
|
53
|
+
) from e
|
|
55
54
|
except Exception as e:
|
|
56
55
|
raise DSWCommunicationError(
|
|
57
56
|
reason='Communication error',
|
|
58
57
|
message=f'Communication with server failed ({e})'
|
|
59
|
-
)
|
|
58
|
+
) from e
|
|
60
59
|
return handled_client_call
|
|
61
60
|
|
|
62
61
|
|
|
62
|
+
# pylint: disable-next=too-many-public-methods
|
|
63
63
|
class DSWAPIClient:
|
|
64
64
|
|
|
65
65
|
def _headers(self, extra=None):
|
|
@@ -91,7 +91,9 @@ class DSWAPIClient:
|
|
|
91
91
|
"""
|
|
92
92
|
self.api_url = api_url
|
|
93
93
|
self.token = api_key
|
|
94
|
-
self.session = session or aiohttp.ClientSession(
|
|
94
|
+
self.session = session or aiohttp.ClientSession(
|
|
95
|
+
connector=aiohttp.TCPConnector(ssl=False),
|
|
96
|
+
)
|
|
95
97
|
|
|
96
98
|
@property
|
|
97
99
|
def templates_endpoint(self):
|
|
@@ -112,31 +114,48 @@ class DSWAPIClient:
|
|
|
112
114
|
return False
|
|
113
115
|
|
|
114
116
|
async def _post_json(self, endpoint, json) -> dict:
|
|
115
|
-
async with self.session.post(
|
|
117
|
+
async with self.session.post(
|
|
118
|
+
url=f'{self.api_url}{endpoint}',
|
|
119
|
+
json=json,
|
|
120
|
+
headers=self._headers(),
|
|
121
|
+
) as r:
|
|
116
122
|
self._check_status(r, expected_status=201)
|
|
117
123
|
return await r.json()
|
|
118
124
|
|
|
119
125
|
async def _put_json(self, endpoint, json) -> dict:
|
|
120
|
-
async with self.session.put(
|
|
126
|
+
async with self.session.put(
|
|
127
|
+
url=f'{self.api_url}{endpoint}',
|
|
128
|
+
json=json,
|
|
129
|
+
headers=self._headers(),
|
|
130
|
+
) as r:
|
|
121
131
|
self._check_status(r, expected_status=200)
|
|
122
132
|
return await r.json()
|
|
123
133
|
|
|
124
134
|
async def _get_json(self, endpoint) -> dict:
|
|
125
|
-
async with self.session.get(
|
|
135
|
+
async with self.session.get(
|
|
136
|
+
url=f'{self.api_url}{endpoint}',
|
|
137
|
+
headers=self._headers(),
|
|
138
|
+
) as r:
|
|
126
139
|
self._check_status(r, expected_status=200)
|
|
127
140
|
return await r.json()
|
|
128
141
|
|
|
129
142
|
async def _get_bytes(self, endpoint) -> bytes:
|
|
130
|
-
async with self.session.get(
|
|
143
|
+
async with self.session.get(
|
|
144
|
+
url=f'{self.api_url}{endpoint}',
|
|
145
|
+
headers=self._headers(),
|
|
146
|
+
) as r:
|
|
131
147
|
self._check_status(r, expected_status=200)
|
|
132
148
|
return await r.read()
|
|
133
149
|
|
|
134
150
|
async def _delete(self, endpoint) -> bool:
|
|
135
|
-
async with self.session.delete(
|
|
151
|
+
async with self.session.delete(
|
|
152
|
+
url=f'{self.api_url}{endpoint}',
|
|
153
|
+
headers=self._headers(),
|
|
154
|
+
) as r:
|
|
136
155
|
return r.status == 204
|
|
137
156
|
|
|
138
157
|
@handle_client_errors
|
|
139
|
-
async def login(self, email: str, password: str) ->
|
|
158
|
+
async def login(self, email: str, password: str) -> str | None:
|
|
140
159
|
req = {'email': email, 'password': password}
|
|
141
160
|
body = await self._post_json('/tokens', json=req)
|
|
142
161
|
self.token = body.get('token', None)
|
|
@@ -148,7 +167,10 @@ class DSWAPIClient:
|
|
|
148
167
|
|
|
149
168
|
@handle_client_errors
|
|
150
169
|
async def check_template_exists(self, remote_id: str) -> bool:
|
|
151
|
-
async with self.session.get(
|
|
170
|
+
async with self.session.get(
|
|
171
|
+
url=f'{self.templates_endpoint}/{remote_id}',
|
|
172
|
+
headers=self._headers(),
|
|
173
|
+
) as r:
|
|
152
174
|
if r.status == 404:
|
|
153
175
|
return False
|
|
154
176
|
self._check_status(r, expected_status=200)
|
|
@@ -156,20 +178,22 @@ class DSWAPIClient:
|
|
|
156
178
|
|
|
157
179
|
@handle_client_errors
|
|
158
180
|
async def check_draft_exists(self, remote_id: str) -> bool:
|
|
159
|
-
async with self.session.get(
|
|
181
|
+
async with self.session.get(
|
|
182
|
+
url=f'{self.drafts_endpoint}/{remote_id}',
|
|
183
|
+
headers=self._headers(),
|
|
184
|
+
) as r:
|
|
160
185
|
if r.status == 404:
|
|
161
186
|
return False
|
|
162
187
|
self._check_status(r, expected_status=200)
|
|
163
188
|
return True
|
|
164
189
|
|
|
165
190
|
@handle_client_errors
|
|
166
|
-
async def get_templates(self) ->
|
|
191
|
+
async def get_templates(self) -> list[Template]:
|
|
167
192
|
body = await self._get_json('/document-templates/all')
|
|
168
193
|
return list(map(_load_remote_template, body))
|
|
169
194
|
|
|
170
195
|
@handle_client_errors
|
|
171
|
-
async def get_drafts(self) ->
|
|
172
|
-
# TODO: better paging
|
|
196
|
+
async def get_drafts(self) -> list[Template]:
|
|
173
197
|
body = await self._get_json('/document-template-drafts?size=10000')
|
|
174
198
|
drafts = body.get('_embedded', {}).get('documentTemplateDrafts', [])
|
|
175
199
|
return list(map(_load_remote_template, drafts))
|
|
@@ -191,7 +215,7 @@ class DSWAPIClient:
|
|
|
191
215
|
return _load_remote_template(body)
|
|
192
216
|
|
|
193
217
|
@handle_client_errors
|
|
194
|
-
async def get_template_draft_files(self, remote_id: str) ->
|
|
218
|
+
async def get_template_draft_files(self, remote_id: str) -> list[TemplateFile]:
|
|
195
219
|
body = await self._get_json(f'/document-template-drafts/{remote_id}/files')
|
|
196
220
|
result = []
|
|
197
221
|
for file_body in body:
|
|
@@ -206,7 +230,7 @@ class DSWAPIClient:
|
|
|
206
230
|
return _load_remote_file(body)
|
|
207
231
|
|
|
208
232
|
@handle_client_errors
|
|
209
|
-
async def get_template_draft_assets(self, remote_id: str) ->
|
|
233
|
+
async def get_template_draft_assets(self, remote_id: str) -> list[TemplateFile]:
|
|
210
234
|
body = await self._get_json(f'/document-template-drafts/{remote_id}/assets')
|
|
211
235
|
result = []
|
|
212
236
|
for file_body in body:
|
|
@@ -217,8 +241,10 @@ class DSWAPIClient:
|
|
|
217
241
|
|
|
218
242
|
@handle_client_errors
|
|
219
243
|
async def get_template_draft_asset(self, remote_id: str, asset_id: str) -> TemplateFile:
|
|
220
|
-
body = await self._get_json(f'/document-template-drafts/{remote_id}
|
|
221
|
-
|
|
244
|
+
body = await self._get_json(f'/document-template-drafts/{remote_id}'
|
|
245
|
+
f'/assets/{asset_id}')
|
|
246
|
+
content = await self._get_bytes(f'/document-template-drafts/{remote_id}'
|
|
247
|
+
f'/assets/{asset_id}/content')
|
|
222
248
|
return _load_remote_asset(body, content)
|
|
223
249
|
|
|
224
250
|
@handle_client_errors
|
|
@@ -258,7 +284,8 @@ class DSWAPIClient:
|
|
|
258
284
|
async def put_template_draft_file_content(self, remote_id: str, tfile: TemplateFile):
|
|
259
285
|
self.session.headers.update(self._headers())
|
|
260
286
|
async with self.session.put(
|
|
261
|
-
f'{self.api_url}/document-template-drafts/{remote_id}
|
|
287
|
+
f'{self.api_url}/document-template-drafts/{remote_id}'
|
|
288
|
+
f'/files/{tfile.remote_id}/content',
|
|
262
289
|
data=tfile.content,
|
|
263
290
|
headers={'Content-Type': 'text/plain;charset=UTF-8'},
|
|
264
291
|
) as r:
|
|
@@ -302,7 +329,8 @@ class DSWAPIClient:
|
|
|
302
329
|
value=tfile.filename.as_posix(),
|
|
303
330
|
)
|
|
304
331
|
async with self.session.put(
|
|
305
|
-
f'{self.api_url}/document-template-drafts/{remote_id}
|
|
332
|
+
f'{self.api_url}/document-template-drafts/{remote_id}'
|
|
333
|
+
f'/assets/{tfile.remote_id}/content',
|
|
306
334
|
data=data,
|
|
307
335
|
headers=self._headers()
|
|
308
336
|
) as r:
|
|
@@ -338,11 +366,11 @@ class DSWAPIClient:
|
|
|
338
366
|
|
|
339
367
|
|
|
340
368
|
def _load_remote_file(data: dict) -> TemplateFile:
|
|
341
|
-
content = data.get('content',
|
|
342
|
-
filename = str(data.get('fileName', ''))
|
|
369
|
+
content: str = data.get('content', '')
|
|
370
|
+
filename: str = str(data.get('fileName', ''))
|
|
343
371
|
template_file = TemplateFile(
|
|
344
372
|
remote_id=data.get('uuid', None),
|
|
345
|
-
remote_type=TemplateFileType.
|
|
373
|
+
remote_type=TemplateFileType.FILE,
|
|
346
374
|
filename=pathlib.Path(urllib.parse.unquote(filename)),
|
|
347
375
|
content=content.encode(encoding=DEFAULT_ENCODING),
|
|
348
376
|
)
|
|
@@ -353,7 +381,7 @@ def _load_remote_asset(data: dict, content: bytes) -> TemplateFile:
|
|
|
353
381
|
filename = str(data.get('fileName', ''))
|
|
354
382
|
template_file = TemplateFile(
|
|
355
383
|
remote_id=data.get('uuid', None),
|
|
356
|
-
remote_type=TemplateFileType.
|
|
384
|
+
remote_type=TemplateFileType.ASSET,
|
|
357
385
|
filename=pathlib.Path(urllib.parse.unquote(filename)),
|
|
358
386
|
content_type=data.get('contentType', None),
|
|
359
387
|
content=content,
|
|
@@ -9,9 +9,9 @@ BuildInfo = namedtuple(
|
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
BUILD_INFO = BuildInfo(
|
|
12
|
-
version='v4.
|
|
13
|
-
built_at='
|
|
14
|
-
sha='
|
|
12
|
+
version='v4.14.0~213910f',
|
|
13
|
+
built_at='2025-01-07 08:18:02Z',
|
|
14
|
+
sha='213910ffb32a7cea98942ccd0f6c52cb6cf79128',
|
|
15
15
|
branch='HEAD',
|
|
16
|
-
tag='v4.
|
|
16
|
+
tag='v4.14.0',
|
|
17
17
|
)
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
+
# pylint: disable=too-many-positional-arguments
|
|
1
2
|
import asyncio
|
|
2
|
-
import signal
|
|
3
|
-
|
|
4
|
-
import click # type: ignore
|
|
5
3
|
import datetime
|
|
6
|
-
import dotenv
|
|
7
|
-
import humanize # type: ignore
|
|
8
4
|
import logging
|
|
9
5
|
import mimetypes
|
|
10
6
|
import pathlib
|
|
11
|
-
import
|
|
12
|
-
import
|
|
7
|
+
import signal
|
|
8
|
+
import sys
|
|
13
9
|
|
|
14
|
-
|
|
10
|
+
import click
|
|
11
|
+
import dotenv
|
|
12
|
+
import humanize
|
|
13
|
+
import slugify
|
|
14
|
+
import watchfiles
|
|
15
15
|
|
|
16
16
|
from .api_client import DSWCommunicationError
|
|
17
17
|
from .core import TDKCore, TDKProcessingError
|
|
@@ -43,7 +43,7 @@ class ClickPrinter:
|
|
|
43
43
|
|
|
44
44
|
@staticmethod
|
|
45
45
|
def warning(message: str, **kwargs):
|
|
46
|
-
click.secho('WARNING', fg='yellow', bold=True, nl=False)
|
|
46
|
+
click.secho('WARNING', fg='yellow', bold=True, nl=False, **kwargs)
|
|
47
47
|
click.echo(f': {message}')
|
|
48
48
|
|
|
49
49
|
@staticmethod
|
|
@@ -62,7 +62,8 @@ class ClickPrinter:
|
|
|
62
62
|
click.echo(f': {message}')
|
|
63
63
|
|
|
64
64
|
@classmethod
|
|
65
|
-
def watch_change(cls, change_type: watchfiles.Change, filepath: pathlib.Path,
|
|
65
|
+
def watch_change(cls, change_type: watchfiles.Change, filepath: pathlib.Path,
|
|
66
|
+
root: pathlib.Path):
|
|
66
67
|
timestamp = datetime.datetime.now().isoformat(timespec='milliseconds')
|
|
67
68
|
sign = cls.CHANGE_SIGNS[change_type]
|
|
68
69
|
click.secho('WATCH', fg='blue', bold=True, nl=False)
|
|
@@ -93,10 +94,12 @@ def print_template_info(template: Template):
|
|
|
93
94
|
click.echo(f' - {tfile.filename.as_posix()} [{filesize}]')
|
|
94
95
|
|
|
95
96
|
|
|
97
|
+
# pylint: disable-next=unused-argument
|
|
96
98
|
def rectify_url(ctx, param, value) -> str:
|
|
97
99
|
return value.rstrip('/')
|
|
98
100
|
|
|
99
101
|
|
|
102
|
+
# pylint: disable-next=unused-argument
|
|
100
103
|
def rectify_key(ctx, param, value) -> str:
|
|
101
104
|
return value.strip()
|
|
102
105
|
|
|
@@ -130,7 +133,7 @@ class ClickLogger(logging.Logger):
|
|
|
130
133
|
name = logging.getLevelName(level) # type: str
|
|
131
134
|
if justify:
|
|
132
135
|
name = name.ljust(8, ' ')
|
|
133
|
-
if self.colors and level in self.LEVEL_STYLES
|
|
136
|
+
if self.colors and level in self.LEVEL_STYLES:
|
|
134
137
|
name = self.LEVEL_STYLES[level](name)
|
|
135
138
|
return name
|
|
136
139
|
|
|
@@ -143,6 +146,7 @@ class ClickLogger(logging.Logger):
|
|
|
143
146
|
click.echo(self._format_level(level, justify=self.show_timestamp) + sep, nl=False)
|
|
144
147
|
click.echo(message)
|
|
145
148
|
|
|
149
|
+
# pylint: disable-next=unused-argument
|
|
146
150
|
def _log(self, level, msg, *args, **kwargs):
|
|
147
151
|
if not self.muted:
|
|
148
152
|
# super()._log(level, msg, args, exc_info, extra, stack_info, stacklevel)
|
|
@@ -165,9 +169,9 @@ class AliasedGroup(click.Group):
|
|
|
165
169
|
if x.startswith(cmd_name)]
|
|
166
170
|
if not matches:
|
|
167
171
|
return None
|
|
168
|
-
|
|
172
|
+
if len(matches) == 1:
|
|
169
173
|
return click.Group.get_command(self, ctx, matches[0])
|
|
170
|
-
ctx.fail('Too many matches:
|
|
174
|
+
return ctx.fail(f'Too many matches: {", ".join(sorted(matches))}')
|
|
171
175
|
|
|
172
176
|
|
|
173
177
|
class CLIContext:
|
|
@@ -184,21 +188,26 @@ class CLIContext:
|
|
|
184
188
|
self.logger.muted = True
|
|
185
189
|
|
|
186
190
|
|
|
187
|
-
def interact_formats() ->
|
|
191
|
+
def interact_formats() -> dict[str, FormatSpec]:
|
|
188
192
|
add_format = click.confirm('Do you want to add a format?', default=True)
|
|
189
|
-
formats
|
|
193
|
+
formats: dict[str, FormatSpec] = {}
|
|
190
194
|
while add_format:
|
|
191
195
|
format_spec = FormatSpec()
|
|
192
196
|
prompt_fill('Format name', obj=format_spec, attr='name', default='HTML')
|
|
193
|
-
if format_spec.name not in formats
|
|
197
|
+
if format_spec.name not in formats or click.confirm(
|
|
194
198
|
'There is already a format with this name. Do you want to change it?'
|
|
195
199
|
):
|
|
196
200
|
prompt_fill('File extension', obj=format_spec, attr='file_extension',
|
|
197
201
|
default=format_spec.name.lower() if ' ' not in format_spec.name else None)
|
|
198
202
|
prompt_fill('Content type', obj=format_spec, attr='content_type',
|
|
199
203
|
default=mimetypes.types_map.get(f'.{format_spec.file_extension}', None))
|
|
200
|
-
|
|
201
|
-
prompt_fill(
|
|
204
|
+
t_path = pathlib.Path('src') / f'template.{format_spec.file_extension}.j2'
|
|
205
|
+
prompt_fill(
|
|
206
|
+
text='Jinja2 filename',
|
|
207
|
+
obj=format_spec,
|
|
208
|
+
attr='filename',
|
|
209
|
+
default=str(t_path),
|
|
210
|
+
)
|
|
202
211
|
formats[format_spec.name] = format_spec
|
|
203
212
|
click.echo('=' * 60)
|
|
204
213
|
add_format = click.confirm('Do you want to add yet another format?', default=False)
|
|
@@ -208,10 +217,14 @@ def interact_formats() -> Dict[str, FormatSpec]:
|
|
|
208
217
|
def interact_builder(builder: TemplateBuilder):
|
|
209
218
|
prompt_fill('Template name', obj=builder, attr='name')
|
|
210
219
|
prompt_fill('Organization ID', obj=builder, attr='organization_id')
|
|
211
|
-
prompt_fill('Template ID', obj=builder, attr='template_id',
|
|
212
|
-
|
|
213
|
-
prompt_fill('
|
|
214
|
-
|
|
220
|
+
prompt_fill('Template ID', obj=builder, attr='template_id',
|
|
221
|
+
default=slugify.slugify(builder.name))
|
|
222
|
+
prompt_fill('Version', obj=builder, attr='version',
|
|
223
|
+
default='0.1.0')
|
|
224
|
+
prompt_fill('Description', obj=builder, attr='description',
|
|
225
|
+
default='My custom template')
|
|
226
|
+
prompt_fill('License', obj=builder, attr='license',
|
|
227
|
+
default='CC0')
|
|
215
228
|
click.echo('=' * 60)
|
|
216
229
|
formats = interact_formats()
|
|
217
230
|
for format_spec in formats.values():
|
|
@@ -224,19 +237,16 @@ def load_local(tdk: TDKCore, template_dir: pathlib.Path):
|
|
|
224
237
|
except Exception as e:
|
|
225
238
|
ClickPrinter.failure('Could not load local template')
|
|
226
239
|
ClickPrinter.error(f'> {e}')
|
|
227
|
-
exit(1)
|
|
240
|
+
sys.exit(1)
|
|
228
241
|
|
|
229
242
|
|
|
230
243
|
def dir_from_id(template_id: str) -> pathlib.Path:
|
|
231
244
|
return pathlib.Path.cwd() / template_id.replace(':', '_')
|
|
232
245
|
|
|
233
246
|
|
|
234
|
-
#############################################################################################################
|
|
235
|
-
|
|
236
|
-
|
|
237
247
|
@click.group(cls=AliasedGroup)
|
|
238
|
-
@click.option('-e', '--dot-env', default='.env', required=False,
|
|
239
|
-
type=click.Path(file_okay=True, dir_okay=False),
|
|
248
|
+
@click.option('-e', '--dot-env', default='.env', required=False,
|
|
249
|
+
show_default=True, type=click.Path(file_okay=True, dir_okay=False),
|
|
240
250
|
help='Provide file with environment variables.')
|
|
241
251
|
@click.option('-q', '--quiet', is_flag=True,
|
|
242
252
|
help='Hide additional information logs.')
|
|
@@ -266,7 +276,7 @@ def new_template(ctx, template_dir, force):
|
|
|
266
276
|
except Exception:
|
|
267
277
|
click.echo('')
|
|
268
278
|
ClickPrinter.failure('Exited...')
|
|
269
|
-
exit(1)
|
|
279
|
+
sys.exit(1)
|
|
270
280
|
tdk = TDKCore(template=builder.build(), logger=ctx.obj.logger)
|
|
271
281
|
template_dir = template_dir or dir_from_id(tdk.safe_template.id)
|
|
272
282
|
tdk.prepare_local(template_dir=template_dir)
|
|
@@ -276,7 +286,7 @@ def new_template(ctx, template_dir, force):
|
|
|
276
286
|
except Exception as e:
|
|
277
287
|
ClickPrinter.failure('Could not create new template project')
|
|
278
288
|
ClickPrinter.error(f'> {e}')
|
|
279
|
-
exit(1)
|
|
289
|
+
sys.exit(1)
|
|
280
290
|
|
|
281
291
|
|
|
282
292
|
@main.command(help='Download template from Wizard.', name='get')
|
|
@@ -309,30 +319,32 @@ def get_template(ctx, api_url, template_id, template_dir, api_key, force):
|
|
|
309
319
|
ClickPrinter.error('Could not get template:', bold=True)
|
|
310
320
|
ClickPrinter.error(f'> {e.reason}\n> {e.message}')
|
|
311
321
|
await tdk.safe_client.close()
|
|
312
|
-
exit(1)
|
|
322
|
+
sys.exit(1)
|
|
313
323
|
await tdk.safe_client.safe_close()
|
|
314
324
|
if template_type == 'draft':
|
|
315
325
|
tdk.prepare_local(template_dir=template_dir)
|
|
316
326
|
try:
|
|
317
327
|
tdk.store_local(force=force)
|
|
318
|
-
ClickPrinter.success(f'Template draft {template_id}
|
|
328
|
+
ClickPrinter.success(f'Template draft {template_id} '
|
|
329
|
+
f'downloaded to {template_dir}')
|
|
319
330
|
except Exception as e:
|
|
320
331
|
ClickPrinter.failure('Could not store template locally')
|
|
321
332
|
ClickPrinter.error(f'> {e}')
|
|
322
333
|
await tdk.safe_client.close()
|
|
323
|
-
exit(1)
|
|
334
|
+
sys.exit(1)
|
|
324
335
|
elif template_type == 'bundle' and zip_data is not None:
|
|
325
336
|
try:
|
|
326
337
|
tdk.extract_package(zip_data=zip_data, template_dir=template_dir, force=force)
|
|
327
|
-
ClickPrinter.success(f'Template {template_id} (released)
|
|
338
|
+
ClickPrinter.success(f'Template {template_id} (released) '
|
|
339
|
+
f'downloaded to {template_dir}')
|
|
328
340
|
except Exception as e:
|
|
329
341
|
ClickPrinter.failure('Could not store template locally')
|
|
330
342
|
ClickPrinter.error(f'> {e}')
|
|
331
343
|
await tdk.safe_client.close()
|
|
332
|
-
exit(1)
|
|
344
|
+
sys.exit(1)
|
|
333
345
|
else:
|
|
334
346
|
ClickPrinter.failure(f'{template_id} is not released nor draft of a document template')
|
|
335
|
-
exit(1)
|
|
347
|
+
sys.exit(1)
|
|
336
348
|
|
|
337
349
|
loop = asyncio.get_event_loop()
|
|
338
350
|
loop.run_until_complete(main_routine())
|
|
@@ -345,8 +357,10 @@ def get_template(ctx, api_url, template_id, template_dir, api_key, force):
|
|
|
345
357
|
@click.option('-k', '--api-key', metavar='API-KEY', envvar='DSW_API_KEY',
|
|
346
358
|
prompt='API Key', help='API key for Wizard instance.', callback=rectify_key,
|
|
347
359
|
hide_input=True)
|
|
348
|
-
@click.option('-f', '--force', is_flag=True,
|
|
349
|
-
|
|
360
|
+
@click.option('-f', '--force', is_flag=True,
|
|
361
|
+
help='Delete template if already exists.')
|
|
362
|
+
@click.option('-w', '--watch', is_flag=True,
|
|
363
|
+
help='Enter watch mode to continually upload changes.')
|
|
350
364
|
@click.pass_context
|
|
351
365
|
def put_template(ctx, api_url, template_dir, api_key, force, watch):
|
|
352
366
|
tdk = TDKCore(logger=ctx.obj.logger)
|
|
@@ -380,7 +394,7 @@ def put_template(ctx, api_url, template_dir, api_key, force, watch):
|
|
|
380
394
|
ClickPrinter.failure('Could not upload template')
|
|
381
395
|
ClickPrinter.error(f'> {e.message}\n> {e.hint}')
|
|
382
396
|
await tdk.safe_client.safe_close()
|
|
383
|
-
exit(1)
|
|
397
|
+
sys.exit(1)
|
|
384
398
|
except DSWCommunicationError as e:
|
|
385
399
|
ClickPrinter.failure('Could not upload template')
|
|
386
400
|
ClickPrinter.error(f'> {e.reason}\n> {e.message}')
|
|
@@ -388,8 +402,9 @@ def put_template(ctx, api_url, template_dir, api_key, force, watch):
|
|
|
388
402
|
'or template already exists...')
|
|
389
403
|
ClickPrinter.error('> Check if you are using the matching version')
|
|
390
404
|
await tdk.safe_client.safe_close()
|
|
391
|
-
exit(1)
|
|
405
|
+
sys.exit(1)
|
|
392
406
|
|
|
407
|
+
# pylint: disable-next=unused-argument
|
|
393
408
|
def set_stop_event(signum, frame):
|
|
394
409
|
signame = signal.Signals(signum).name
|
|
395
410
|
ClickPrinter.warning(f'Got {signame}, finishing... Bye!')
|
|
@@ -417,7 +432,7 @@ def create_package(ctx, template_dir, output, force: bool):
|
|
|
417
432
|
except Exception as e:
|
|
418
433
|
ClickPrinter.failure('Failed to create the package')
|
|
419
434
|
ClickPrinter.error(f'> {e}')
|
|
420
|
-
exit(1)
|
|
435
|
+
sys.exit(1)
|
|
421
436
|
filename = click.style(output, bold=True)
|
|
422
437
|
ClickPrinter.success(f'Package {filename} created')
|
|
423
438
|
|
|
@@ -440,7 +455,7 @@ def extract_package(ctx, template_package, output, force: bool):
|
|
|
440
455
|
except Exception as e:
|
|
441
456
|
ClickPrinter.failure('Failed to extract the package')
|
|
442
457
|
ClickPrinter.error(f'> {e}')
|
|
443
|
-
exit(1)
|
|
458
|
+
sys.exit(1)
|
|
444
459
|
ClickPrinter.success(f'Package {template_package} extracted')
|
|
445
460
|
|
|
446
461
|
|
|
@@ -484,7 +499,7 @@ def list_templates(ctx, api_url, api_key, output_format: str,
|
|
|
484
499
|
ClickPrinter.failure('Failed to get list of templates')
|
|
485
500
|
ClickPrinter.error(f'> {e.reason}\n> {e.message}')
|
|
486
501
|
await tdk.safe_client.safe_close()
|
|
487
|
-
exit(1)
|
|
502
|
+
sys.exit(1)
|
|
488
503
|
|
|
489
504
|
loop = asyncio.get_event_loop()
|
|
490
505
|
loop.run_until_complete(main_routine())
|
|
@@ -529,4 +544,4 @@ def create_dot_env(ctx, template_dir, api_url, api_key, force):
|
|
|
529
544
|
except Exception as e:
|
|
530
545
|
ClickPrinter.failure('Failed to create dot-env file')
|
|
531
546
|
ClickPrinter.error(f'> {e}')
|
|
532
|
-
exit(1)
|
|
547
|
+
sys.exit(1)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import pathlib
|
|
2
|
-
import pathspec # type: ignore
|
|
3
2
|
import re
|
|
4
3
|
|
|
4
|
+
import pathspec
|
|
5
|
+
|
|
5
6
|
APP = 'dsw-tdk'
|
|
6
|
-
VERSION = '4.
|
|
7
|
+
VERSION = '4.14.0'
|
|
7
8
|
METAMODEL_VERSION = 16
|
|
8
9
|
|
|
9
10
|
REGEX_SEMVER = re.compile(r'^[0-9]+\.[0-9]+\.[0-9]+$')
|
|
@@ -17,4 +18,4 @@ DEFAULT_ENCODING = 'utf-8'
|
|
|
17
18
|
DEFAULT_README = pathlib.Path('README.md')
|
|
18
19
|
|
|
19
20
|
TEMPLATE_FILE = 'template.json'
|
|
20
|
-
|
|
21
|
+
PathspecFactory = pathspec.patterns.GitWildMatchPattern
|