oc-cdtapi 3.9.1__py2-none-any.whl → 3.9.4__py2-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.
- oc_cdtapi/API.py +138 -42
- oc_cdtapi/DevPIAPI.py +30 -30
- oc_cdtapi/DmsAPI.py +120 -54
- oc_cdtapi/DmsGetverAPI.py +218 -155
- oc_cdtapi/ForemanAPI.py +495 -546
- oc_cdtapi/JenkinsAPI.py +171 -160
- oc_cdtapi/NexusAPI.py +1 -2
- {oc_cdtapi-3.9.1.dist-info → oc_cdtapi-3.9.4.dist-info}/METADATA +2 -2
- oc_cdtapi-3.9.4.dist-info/RECORD +15 -0
- oc_cdtapi-3.9.1.dist-info/RECORD +0 -15
- {oc_cdtapi-3.9.1.data → oc_cdtapi-3.9.4.data}/scripts/nexus.py +0 -0
- {oc_cdtapi-3.9.1.dist-info → oc_cdtapi-3.9.4.dist-info}/LICENSE +0 -0
- {oc_cdtapi-3.9.1.dist-info → oc_cdtapi-3.9.4.dist-info}/WHEEL +0 -0
- {oc_cdtapi-3.9.1.dist-info → oc_cdtapi-3.9.4.dist-info}/top_level.txt +0 -0
oc_cdtapi/API.py
CHANGED
@@ -13,6 +13,7 @@ if sys.version_info.major == 2:
|
|
13
13
|
elif sys.version_info.major == 3:
|
14
14
|
strtype = str
|
15
15
|
|
16
|
+
|
16
17
|
class HttpAPIError(Exception):
|
17
18
|
|
18
19
|
def __init__(self, code=0, url='', resp=None, text=''):
|
@@ -21,7 +22,6 @@ class HttpAPIError(Exception):
|
|
21
22
|
self.resp = resp
|
22
23
|
self.text = text
|
23
24
|
|
24
|
-
|
25
25
|
def __str__(self):
|
26
26
|
return self.text + ': Code ' + str(self.code) + ' ' + self.url
|
27
27
|
|
@@ -40,11 +40,11 @@ class HttpAPI(object):
|
|
40
40
|
|
41
41
|
def __init__(self, root=None, user=None, auth=None, readonly=False, anonymous=False):
|
42
42
|
"""
|
43
|
-
:param root: Root URL (uses *_URL by default)
|
44
|
-
:param user: Username (uses *_USER by default)
|
45
|
-
:param auth: Password (uses *_PASSWORD by default)
|
46
|
-
:param readonly: sets readonly property, should be explictly supported by child
|
47
|
-
:param anonymous: ignore username/password provided and use anonymous requests
|
43
|
+
:param str root: Root URL (uses *_URL by default)
|
44
|
+
:param str user: Username (uses *_USER by default)
|
45
|
+
:param str auth: Password (uses *_PASSWORD by default)
|
46
|
+
:param bool readonly: sets readonly property, should be explictly supported by child
|
47
|
+
:param bool anonymous: ignore username/password provided and use anonymous requests
|
48
48
|
|
49
49
|
>>> import os
|
50
50
|
>>> from oc_cdtapi.API import HttpAPI
|
@@ -67,30 +67,58 @@ class HttpAPI(object):
|
|
67
67
|
"""
|
68
68
|
|
69
69
|
self.readonly = readonly
|
70
|
-
|
71
|
-
|
72
|
-
if not
|
73
|
-
|
74
|
-
|
70
|
+
|
71
|
+
if self._env_prefix is not None:
|
72
|
+
if not root:
|
73
|
+
root = os.getenv(self._env_prefix + self._env_url)
|
74
|
+
|
75
|
+
if not user:
|
76
|
+
user = os.getenv(self._env_prefix + self._env_user)
|
77
|
+
|
78
|
+
if not auth:
|
79
|
+
auth = os.getenv(self._env_prefix + self._env_auth)
|
80
|
+
|
81
|
+
if not root:
|
82
|
+
raise ValueError('Server URL for service [%s] not set' % self._env_prefix)
|
83
|
+
|
75
84
|
self.root = root
|
76
85
|
self.web = requests.Session()
|
77
|
-
if user != None and not anonymous: self.web.auth = (user, auth)
|
78
86
|
|
79
|
-
if
|
80
|
-
self.web.
|
87
|
+
if user and not anonymous:
|
88
|
+
self.web.auth = (user, auth)
|
81
89
|
|
90
|
+
if self.root.startswith("https:"):
|
91
|
+
# ignore self-signed certificates warning
|
92
|
+
self.web.verify = False
|
82
93
|
|
83
94
|
def set_readonly(self, readonly=True):
|
95
|
+
"""
|
96
|
+
Set instance as read-only
|
97
|
+
:param bool readonly: foo
|
98
|
+
"""
|
84
99
|
self.readonly = readonly
|
85
100
|
|
86
|
-
|
87
101
|
def re(self, req):
|
88
|
-
"""
|
89
|
-
|
102
|
+
"""
|
103
|
+
Constructs request URL from request name
|
104
|
+
:param str req: string or list of strings for the request
|
105
|
+
"""
|
106
|
+
# do not use 'urlparse.urljoin' here because it gives wrong result if 'root'
|
107
|
+
# contains sub-path. Example:
|
108
|
+
# urlparse.urljoin("https://exapmle.com:5400/c1/c2/c3", "c4/c5/c6")
|
109
|
+
# gives 'https://exapmle.com:5400/c1/c2/c4/c5/c6'
|
110
|
+
# while 'https://exapmle.com:5400/c1/c2/c3/c4/c5/c6' expected ('c3' missing)
|
111
|
+
if isinstance(req, list):
|
112
|
+
req = posixpath.sep.join(req)
|
90
113
|
|
114
|
+
return posixpath.join(self.root, req)
|
91
115
|
|
92
116
|
def pp(self, resp, write_to=None, stream=False, **kvarg):
|
93
117
|
""" Post-processes response
|
118
|
+
:param requests.Response resp: the response object
|
119
|
+
:param fileObj write_to: file object to write result to
|
120
|
+
:param bool stream: use stream mode (useful for large objects)
|
121
|
+
:param kvarg: another keyword arguments, not actually used
|
94
122
|
|
95
123
|
New feature! If write_to parameter is given to any method, that returns response
|
96
124
|
object, request result will be written to fd or filename specified in write_to.
|
@@ -147,29 +175,50 @@ class HttpAPI(object):
|
|
147
175
|
|
148
176
|
if resp.status_code < self.raise_exception_low or resp.status_code > self.raise_exception_high:
|
149
177
|
raise self._error(resp.status_code, resp.url, resp, 'Error making request to server')
|
150
|
-
|
151
|
-
|
178
|
+
|
179
|
+
if write_to is not None:
|
180
|
+
|
181
|
+
if isinstance(write_to, strtype):
|
152
182
|
fd = open(write_to, 'wb')
|
153
183
|
else:
|
154
184
|
fd = write_to
|
185
|
+
|
155
186
|
if not stream:
|
156
187
|
fd.write(resp.content)
|
157
188
|
else:
|
158
189
|
shutil.copyfileobj(resp.raw, fd)
|
190
|
+
|
159
191
|
fd.flush()
|
160
|
-
|
192
|
+
|
193
|
+
if isinstance(write_to, strtype):
|
194
|
+
fd.close()
|
195
|
+
|
161
196
|
del fd
|
162
|
-
return resp
|
163
197
|
|
198
|
+
return resp
|
164
199
|
|
165
200
|
def __kvarg(self, kvarg):
|
201
|
+
"""
|
202
|
+
Omit 'write_to' argument
|
203
|
+
:param dict kvarg: keyword arguments
|
204
|
+
:return dict: ditionary without 'write_to' key
|
205
|
+
"""
|
206
|
+
|
166
207
|
kvarg = kvarg.copy()
|
167
|
-
if 'write_to' in kvarg:
|
168
|
-
|
208
|
+
if 'write_to' in kvarg:
|
209
|
+
del kvarg['write_to']
|
169
210
|
|
211
|
+
return kvarg
|
170
212
|
|
171
213
|
def get(self, req, params=None, files=None, data=None, headers=None, **kvarg):
|
172
214
|
"""Sends GET request
|
215
|
+
:param str req: request sub-URL
|
216
|
+
:param dict params: additional GET parameters
|
217
|
+
:param files: files to append to the request
|
218
|
+
:param data: additional data to append to the request
|
219
|
+
:param dict headers: additional headers for the request
|
220
|
+
:param kvarg: additional keyword arguments
|
221
|
+
:return requests.Response: postprocessed the response object
|
173
222
|
|
174
223
|
>>> def test(port):
|
175
224
|
... api = HttpAPI('http://127.0.0.1:' + str(port))
|
@@ -193,18 +242,36 @@ class HttpAPI(object):
|
|
193
242
|
|
194
243
|
"""
|
195
244
|
|
196
|
-
resp = self.web.get(self.re(req), params=params, data=data,
|
197
|
-
|
245
|
+
resp = self.web.get(self.re(req), params=params, data=data,
|
246
|
+
files=files, headers=headers, **self.__kvarg(kvarg))
|
198
247
|
|
248
|
+
return self.pp(resp, **kvarg)
|
199
249
|
|
200
250
|
def post(self, req, params=None, files=None, data=None, headers=None, **kvarg):
|
201
|
-
"""
|
202
|
-
|
251
|
+
"""
|
252
|
+
Sends POST request
|
253
|
+
:param str req: request sub-URL
|
254
|
+
:param dict params: additional GET parameters
|
255
|
+
:param files: files to append to the request
|
256
|
+
:param data: additional data to append to the request
|
257
|
+
:param dict headers: additional headers for the request
|
258
|
+
:param kvarg: additional keyword arguments
|
259
|
+
:return requests.Response: postprocessed the response object
|
260
|
+
"""
|
261
|
+
resp = self.web.post(self.re(req), params=params,
|
262
|
+
data=data, files=files, headers=headers, **kvarg)
|
203
263
|
return self.pp(resp, **kvarg)
|
204
264
|
|
205
|
-
|
206
265
|
def put(self, req, params=None, files=None, data=None, headers=None, **kvarg):
|
207
266
|
"""Sends PUT request
|
267
|
+
:param str req: request sub-URL
|
268
|
+
:param dict params: additional GET parameters
|
269
|
+
:param files: files to append to the request
|
270
|
+
:param data: additional data to append to the request
|
271
|
+
:param dict headers: additional headers for the request
|
272
|
+
:param kvarg: additional keyword arguments
|
273
|
+
:return requests.Response: postprocessed the response object
|
274
|
+
|
208
275
|
>>> def test(port):
|
209
276
|
... api = HttpAPI('http://127.0.0.1:' + str(port))
|
210
277
|
... api.put('getrequest')
|
@@ -215,35 +282,64 @@ class HttpAPI(object):
|
|
215
282
|
|
216
283
|
"""
|
217
284
|
|
218
|
-
resp = self.web.put(self.re(req), params=params,
|
285
|
+
resp = self.web.put(self.re(req), params=params,
|
286
|
+
data=data, files=files, headers=headers, **kvarg)
|
219
287
|
return self.pp(resp, **kvarg)
|
220
288
|
|
221
|
-
|
222
289
|
def delete(self, req, params=None, files=None, data=None, headers=None, **kvarg):
|
223
|
-
"""
|
224
|
-
|
290
|
+
"""
|
291
|
+
Sends DELETE request
|
292
|
+
:param str req: request sub-URL
|
293
|
+
:param dict params: additional GET parameters
|
294
|
+
:param files: files to append to the request
|
295
|
+
:param data: additional data to append to the request
|
296
|
+
:param dict headers: additional headers for the request
|
297
|
+
:param kvarg: additional keyword arguments
|
298
|
+
:return requests.Response: postprocessed the response object
|
299
|
+
"""
|
300
|
+
resp = self.web.delete(self.re(req), params=params,
|
301
|
+
data=data, files=files, headers=headers, **kvarg)
|
225
302
|
return self.pp(resp, **kvarg)
|
226
303
|
|
227
304
|
def head(self, req, params=None, files=None, data=None, headers=None, **kvarg):
|
228
|
-
"""
|
229
|
-
|
305
|
+
"""
|
306
|
+
Sends HEAD request
|
307
|
+
:param str req: request sub-URL
|
308
|
+
:param dict params: additional GET parameters
|
309
|
+
:param files: files to append to the request
|
310
|
+
:param data: additional data to append to the request
|
311
|
+
:param dict headers: additional headers for the request
|
312
|
+
:param kvarg: additional keyword arguments
|
313
|
+
:return requests.Response: postprocessed the response object
|
314
|
+
"""
|
315
|
+
resp = self.web.head(self.re(req), params=params,
|
316
|
+
data=data, files=files, headers=headers, **kvarg)
|
230
317
|
return self.pp(resp, **kvarg)
|
231
318
|
|
232
319
|
|
233
320
|
# Two shortcuts to deal with XML without having full XML support
|
234
321
|
def get_xml_tag(config, tag):
|
235
|
-
"""
|
322
|
+
"""
|
323
|
+
Gets tag value from xml config
|
324
|
+
Warning: this method ignores XML-comments and returns the first inclusion only
|
325
|
+
:param str config: xml string
|
326
|
+
:param str tag: tag to get
|
327
|
+
:return str: first tag value found
|
328
|
+
"""
|
236
329
|
return ((config.split('<' + tag + '>')[1]).split('</' + tag + '>')[0])
|
237
330
|
|
238
331
|
|
239
332
|
def edit_xml_tag(config, tag, value):
|
240
|
-
"""
|
333
|
+
"""
|
334
|
+
Sets tag value in xml config. Works fine if there is only single such tag in XML
|
335
|
+
Warning: this method ignores XML-comments and returns the first inclusion only
|
336
|
+
:param str config: xml string
|
337
|
+
:param str tag: tag to edit
|
338
|
+
:param str value: value to set
|
339
|
+
:return str: str with replaced tag value
|
340
|
+
"""
|
241
341
|
config = config.split('<' + tag + '>')[0] + '<' + tag + '>' + value + '</' + tag + '>' + \
|
242
|
-
|
243
|
-
return config
|
342
|
+
config.split('</' + tag + '>')[1]
|
244
343
|
|
344
|
+
return config
|
245
345
|
|
246
|
-
if __name__ == "__main__":
|
247
|
-
import doctest
|
248
|
-
|
249
|
-
doctest.testmod()
|
oc_cdtapi/DevPIAPI.py
CHANGED
@@ -4,54 +4,54 @@
|
|
4
4
|
Additional HTTP API for Python Package Index based on DevPI
|
5
5
|
"""
|
6
6
|
|
7
|
-
from .API import HttpAPI
|
8
|
-
import re
|
9
|
-
import xml.etree.ElementTree as ET
|
10
|
-
import posixpath
|
7
|
+
from .API import HttpAPI
|
8
|
+
import re
|
9
|
+
import xml.etree.ElementTree as ET
|
10
|
+
import posixpath
|
11
11
|
|
12
|
-
|
12
|
+
|
13
|
+
class DevPIAPI(HttpAPI):
|
13
14
|
"""
|
14
15
|
Class for providing some simple Http requests to PyPI based on DevPI
|
15
16
|
"""
|
16
|
-
_env_prefix = "PYPI_PRODUCTION"
|
17
|
+
_env_prefix = "PYPI_PRODUCTION"
|
17
18
|
|
18
|
-
def _split_pkg_string(
|
19
|
+
def _split_pkg_string(self, name):
|
19
20
|
"""
|
20
21
|
Split string for package name into components following PyPI standard
|
21
|
-
:param
|
22
|
-
:
|
23
|
-
|
24
|
-
:type str_name: string, unicode
|
25
|
-
:return: dictionary with components: distribution, version, build_tag, python_tag, abi_tag, platform_tag. Some of them may be absent
|
22
|
+
:param str name: python package name
|
23
|
+
:return dict: components: distribution, version, build_tag, python_tag, abi_tag, platform_tag.
|
24
|
+
Some of them may be absent
|
26
25
|
"""
|
27
26
|
|
28
|
-
|
27
|
+
_match = re.match(
|
28
|
+
r'^(?P<distribution>[^\.]*)-(?P<version>[\d\.]+)(-(?P<build_tag>[^\-]+))?-(?P<python_tag>[^\-]+)-(?P<abi_tag>[^\-]+)-(?P<platform_tag>[^\-\.]+)\.whl$', name)
|
29
29
|
|
30
|
-
if
|
31
|
-
|
30
|
+
if _match is None:
|
31
|
+
_match = re.match(r'^(?P<distribution>[^\.]*)-(?P<version>[\d\.]+)\.tar\.gz$', name)
|
32
32
|
|
33
|
-
if
|
34
|
-
raise ValueError(
|
33
|
+
if _match is None:
|
34
|
+
raise ValueError('DevPI answer is wrong: package name [%s] does not match any known pattern' % name)
|
35
35
|
|
36
|
-
return
|
36
|
+
return _match.groupdict()
|
37
37
|
|
38
|
-
def get_package_versions(
|
38
|
+
def get_package_versions(self, name):
|
39
39
|
"""
|
40
40
|
Get package version list from server
|
41
|
-
:param
|
42
|
-
:type
|
43
|
-
:param
|
44
|
-
:type
|
41
|
+
:param self: self reference
|
42
|
+
:type self: DevPIApi object
|
43
|
+
:param name: python package name
|
44
|
+
:type name: string, unicode
|
45
45
|
:return: list of versions on the server, or None if nothing found
|
46
46
|
"""
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
name = name.strip()
|
49
|
+
_request = posixpath.join("+simple", name)
|
50
|
+
_resp = self.get(_request)
|
51
51
|
|
52
|
-
if (
|
53
|
-
return None
|
52
|
+
if (_resp.status_code != 200):
|
53
|
+
return None
|
54
54
|
|
55
|
-
xml_resp = ET.fromstring(
|
55
|
+
xml_resp = ET.fromstring(_resp.text)
|
56
56
|
|
57
|
-
return list(
|
57
|
+
return list(set([self._split_pkg_string(x.text)['version'] for x in xml_resp.findall('body/a')]))
|
oc_cdtapi/DmsAPI.py
CHANGED
@@ -1,57 +1,101 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
3
|
import re
|
4
|
+
import posixpath
|
5
|
+
|
6
|
+
from . import API
|
4
7
|
|
5
|
-
from oc_cdtapi import API
|
6
8
|
|
7
9
|
class DmsAPIError(API.HttpAPIError):
|
8
10
|
pass
|
9
11
|
|
10
|
-
|
12
|
+
|
13
|
+
# we use HttpAPI as a base class - the idea of HttpAPI is to use it as a skelet for new API clients
|
14
|
+
class DmsAPI(API.HttpAPI):
|
11
15
|
"""
|
12
16
|
DmsAPI implementation
|
13
17
|
"""
|
14
18
|
# do not forget about docstrings
|
15
19
|
|
16
|
-
|
20
|
+
# this automatically allows usage of DMS_* environment variables - everything is done in HttpAPI for you
|
21
|
+
_env_prefix = 'DMS'
|
17
22
|
_env_token = '_TOKEN'
|
18
|
-
|
23
|
+
# for now we have a separate Components Registry Service for components info obtaining
|
24
|
+
# TODO: refactor when it will be joined with base DMS API on the server-side
|
25
|
+
_env_crs = '_CRS_URL'
|
19
26
|
|
20
27
|
def __init__(self, *args, **argv):
|
21
28
|
"""
|
22
29
|
Initialiazing the parent class then loading the DMS API's bearer token
|
23
30
|
"""
|
24
|
-
|
31
|
+
|
32
|
+
# TODO: re-factor when Python2 support will be deprecated
|
33
|
+
super(DmsAPI, self).__init__(*args, **argv)
|
25
34
|
self.crs_root = os.getenv(self._env_prefix + self._env_crs)
|
26
35
|
token = os.getenv(self._env_prefix + self._env_token)
|
36
|
+
|
27
37
|
if not self.crs_root:
|
28
|
-
raise DmsAPIError("DMS API initialization failed. The components request
|
38
|
+
raise DmsAPIError("DMS API initialization failed. The components request URL [%s] is not set" % (
|
39
|
+
self._env_prefix + self._env_crs))
|
40
|
+
|
29
41
|
if token:
|
30
42
|
self.headers = {"Authorization": "Bearer {}".format(token)}
|
31
43
|
else:
|
32
|
-
|
44
|
+
# Empty headers dict is added for backwards-compatibility with bearer token functional
|
45
|
+
self.headers = {}
|
46
|
+
|
47
|
+
def __req(self, req):
|
48
|
+
"""
|
49
|
+
Joining an URL to one posixpath-compatible
|
50
|
+
:param str req: request, may be list of str
|
51
|
+
:return str: joined req
|
52
|
+
"""
|
53
|
+
if not req:
|
54
|
+
return req
|
55
|
+
|
56
|
+
if isinstance(req, list):
|
57
|
+
logging.log(5, "re-formatting requested list [%s] URL to string" % ', '.join(req))
|
58
|
+
req = posixpath.sep.join(req)
|
59
|
+
|
60
|
+
return req
|
33
61
|
|
34
|
-
if self.crs_root[-1] != '/': self.crs_root += '/'
|
35
62
|
|
36
63
|
def re(self, req):
|
37
64
|
"""
|
38
65
|
Re-defines default request formater, not to be called directly
|
39
|
-
This forms request URL separated by
|
66
|
+
This forms request URL separated by slash from string array
|
67
|
+
:param req: list of str or str for sub-url
|
68
|
+
:return str: full joined URL
|
40
69
|
"""
|
41
|
-
if len(req) == 0: return self.root
|
42
|
-
return self.root + 'dms-service/rest/api/' + '/'.join(req)
|
43
70
|
|
44
|
-
|
45
|
-
|
46
|
-
|
71
|
+
if not req:
|
72
|
+
return self.root
|
73
|
+
|
74
|
+
# do not use 'urlparse.urljoin' here because it gives wrong result if 'root'
|
75
|
+
# contains sub-path. Example:
|
76
|
+
# urlparse.urljoin("https://exapmle.com:5400/c1/c2/c3", "c4/c5/c6")
|
77
|
+
# gives 'https://exapmle.com:5400/c1/c2/c4/c5/c6'
|
78
|
+
# while 'https://exapmle.com:5400/c1/c2/c3/c4/c5/c6' expected ('c3' missing)
|
79
|
+
|
80
|
+
return posixpath.join(self.root, "dms-service", "rest", "api", self.__req(req))
|
81
|
+
|
82
|
+
# directly accessing class variables outside the class itself is possible, but should be avoided
|
83
|
+
# consider using setters/getters or storing state outside the class
|
84
|
+
|
47
85
|
def crs_re(self, req):
|
48
86
|
"""
|
49
87
|
Forming the correct URL for the DMS Components Registry Service
|
88
|
+
:param req: list of str or str for sub-url
|
89
|
+
:return str: full joined URL
|
50
90
|
"""
|
51
|
-
if
|
52
|
-
|
91
|
+
if not req:
|
92
|
+
return self.crs_root
|
93
|
+
|
94
|
+
# see the note about 'urljoin' in 're' method - the same is applicable here
|
53
95
|
|
54
|
-
|
96
|
+
return posixpath.join(self.crs_root, 'rest', 'api', self.__req(req))
|
97
|
+
|
98
|
+
def get_artifacts(self, component, version, ctype=None):
|
55
99
|
"""
|
56
100
|
Gets list of artifacts of given component, version and type
|
57
101
|
:param component: dms component name
|
@@ -59,25 +103,32 @@ class DmsAPI(API.HttpAPI): # we use HttpAPI as a base class - the idea of Http
|
|
59
103
|
:param ctype: type of artifact. if not specified - query all known types
|
60
104
|
:returns: list of artifacts
|
61
105
|
"""
|
62
|
-
assert bool(re.match('^[a-zA-Z0-9_-]*$', component)
|
63
|
-
|
106
|
+
assert bool(re.match('^[a-zA-Z0-9_-]*$', component)
|
107
|
+
), "Component name must contain only latin letters, numbers, underscores and hyphens"
|
108
|
+
assert bool(re.match('^[a-zA-Z0-9._-]*$', version)
|
109
|
+
), "Version must contain only latin letters, numbers, underscores, hyphens and dots"
|
64
110
|
logging.debug('Reached %s.get_artifacts', self.__class__.__name__)
|
111
|
+
|
65
112
|
if ctype is None:
|
66
113
|
types = self.get_types()
|
67
114
|
else:
|
68
|
-
assert bool(re.match('^[a-zA-Z0-9_-]*$', ctype)
|
69
|
-
|
115
|
+
assert bool(re.match('^[a-zA-Z0-9_-]*$', ctype)
|
116
|
+
), "Component type must contain only latin letters, numbers, underscores and hyphens"
|
117
|
+
types = [ctype]
|
70
118
|
|
71
119
|
artifacts = []
|
72
120
|
|
73
121
|
for t in types:
|
74
122
|
req = ['2', 'component', component, 'version', version, t, 'list']
|
75
|
-
|
123
|
+
|
124
|
+
# Why do you use write_to parameter instead of just using response object ?
|
76
125
|
# also, requests has json parser, no need to re-invent it
|
126
|
+
artifacts += self.get(req, headers=self.headers, verify=False).json()
|
77
127
|
|
78
|
-
|
79
|
-
return artifacts
|
128
|
+
# logging has its own format-string engine
|
129
|
+
logging.debug('About to return an array of %d elements', len(artifacts))
|
80
130
|
|
131
|
+
return artifacts
|
81
132
|
|
82
133
|
def get_components(self):
|
83
134
|
"""
|
@@ -88,71 +139,86 @@ class DmsAPI(API.HttpAPI): # we use HttpAPI as a base class - the idea of Http
|
|
88
139
|
req = ['1', 'components']
|
89
140
|
|
90
141
|
crs_request_url = self.crs_re(req)
|
91
|
-
components = self.web.get(crs_request_url, verify=False).json()
|
142
|
+
components = self.web.get(crs_request_url, verify=False).json().get('components', list())
|
92
143
|
logging.debug('About to return an array of %d elements', len(components))
|
144
|
+
|
93
145
|
return components
|
94
146
|
|
95
147
|
def get_gav(self, component, version, ctype, artifact, classifier=None):
|
96
148
|
"""
|
97
149
|
Requests and forms gav for specified artifact
|
98
|
-
:param component: component name
|
99
|
-
:param version: version
|
100
|
-
:param ctype:
|
101
|
-
:param artifact:
|
102
|
-
:param classifier:
|
103
|
-
:returns: gav
|
104
|
-
"""
|
105
|
-
assert bool(re.match('^[a-zA-Z0-9_-]+$', component)
|
106
|
-
|
107
|
-
assert bool(re.match('^[a-zA-Z0-
|
108
|
-
|
150
|
+
:param str component: component name
|
151
|
+
:param str version: version
|
152
|
+
:param str ctype:
|
153
|
+
:param str artifact:
|
154
|
+
:param str classifier:
|
155
|
+
:returns str: gav
|
156
|
+
"""
|
157
|
+
assert bool(re.match('^[a-zA-Z0-9_-]+$', component)
|
158
|
+
), "Component name have not to be empty and must contain only latin letters, numbers, underscores and hyphens"
|
159
|
+
assert bool(re.match('^[a-zA-Z0-9\._-]+$', version)
|
160
|
+
), "Version have not to be empty and must contain only latin letters, numbers, underscores, hyphens and dots"
|
161
|
+
assert bool(re.match('^[a-zA-Z0-9_-]+$', ctype)
|
162
|
+
), "Component type have not to be empty and must contain only latin letters, numbers, underscores and hyphens"
|
163
|
+
assert bool(re.match('^[a-zA-Z0-9_-]+$', artifact)
|
164
|
+
), "Artifact type have not to be empty and must contain only latin letters, numbers, underscores and hyphens"
|
109
165
|
logging.debug('Reached %s.get_gav', self.__class__.__name__)
|
110
166
|
logging.debug('component: {0}'.format(component))
|
111
167
|
logging.debug('version: {0}'.format(version))
|
112
168
|
logging.debug('artifact: {0}'.format(artifact))
|
113
169
|
logging.debug('classifier: {0}'.format(classifier))
|
114
170
|
req = ['1', 'component', component, 'version', version, ctype, artifact, 'gav']
|
171
|
+
|
172
|
+
params = None
|
173
|
+
|
115
174
|
if classifier:
|
116
|
-
assert bool(re.match('^[a-zA-Z0-9_-]+$', classifier)
|
175
|
+
assert bool(re.match('^[a-zA-Z0-9_-]+$', classifier)
|
176
|
+
), "Non-empty classifier must contain only latin letters, hyphens and underscores"
|
117
177
|
params = {'classifier': classifier}
|
118
|
-
else:
|
119
|
-
params = None
|
120
178
|
|
121
179
|
gav = self.get(req, params, headers=self.headers).json()
|
122
180
|
|
123
|
-
assert bool(re.match('^[a-zA-Z0-9\._-]+$', gav['groupId'])
|
124
|
-
|
125
|
-
assert bool(re.match('^[a-zA-Z0-
|
126
|
-
|
181
|
+
assert bool(re.match('^[a-zA-Z0-9\._-]+$', gav['groupId'])
|
182
|
+
), "groupId have not to be empty and must contain only latin letters, numbers, underscores, hyphens and dots"
|
183
|
+
assert bool(re.match('^[a-zA-Z0-9_-]+$', gav['artifactId'])
|
184
|
+
), "artifactId have not to be empty and must contain only latin letters, numbers, underscores and hyphens"
|
185
|
+
assert bool(re.match('^[a-zA-Z0-9\._-]+$', gav['version'])
|
186
|
+
), "version have not to be empty and must contain only latin letters, numbers, underscores, hyphens and dots"
|
187
|
+
assert bool(re.match('^[a-zA-Z0-9_-]+$', gav['packaging'])
|
188
|
+
), "packaging have not to be empty and must contain only latin letters, hyphens and underscores"
|
189
|
+
|
190
|
+
_gav = ':'.join(list(map(lambda x: gav[x], ['groupId', 'artifactId', 'version', 'packaging'])))
|
127
191
|
|
128
|
-
_gav = ':'.join( [gav['groupId'], gav['artifactId'], gav['version'], gav['packaging']] )
|
129
192
|
if gav.get('classifier'):
|
130
|
-
assert bool(re.match('^[a-zA-Z0-9_-]+$', gav['classifier'])
|
193
|
+
assert bool(re.match('^[a-zA-Z0-9_-]+$', gav['classifier'])
|
194
|
+
), "non-empty classifier must contain only latin letters, hyphens and underscores"
|
131
195
|
_gav = ':'.join([_gav, gav['classifier']])
|
132
196
|
|
133
197
|
logging.debug('Formed gav: %s', _gav)
|
134
198
|
|
135
199
|
return _gav
|
136
200
|
|
137
|
-
|
138
|
-
|
139
|
-
|
201
|
+
def get_types(self):
|
202
|
+
return ['notes', 'distribution', 'report', 'static', 'documentation']
|
140
203
|
|
141
204
|
def get_versions(self, component):
|
142
205
|
"""
|
143
206
|
fetches list of versions for specified component
|
144
|
-
:param component: component name
|
145
|
-
:returns:
|
207
|
+
:param str component: component name
|
208
|
+
:returns list: versions
|
146
209
|
"""
|
147
|
-
|
210
|
+
|
211
|
+
assert bool(re.match('^[a-zA-Z0-9_-]*$', component)
|
212
|
+
), "Component name must contain only latin letters, numbers, underscores and hyphens"
|
213
|
+
|
148
214
|
logging.debug('Reached %s.get_versions', self.__class__.__name__)
|
149
215
|
|
150
216
|
req = ['2', 'component', component, 'versions']
|
151
217
|
|
152
218
|
versions = list(map(lambda x: x.get('version'), self.get(req, headers=self.headers).json().get('versions')))
|
153
|
-
logging.debug('About to return an array of %d elements',len(versions))
|
154
|
-
return versions
|
219
|
+
logging.debug('About to return an array of %d elements', len(versions))
|
155
220
|
|
221
|
+
return versions
|
156
222
|
|
157
223
|
def ping_dms(self):
|
158
224
|
"""
|
@@ -160,5 +226,5 @@ class DmsAPI(API.HttpAPI): # we use HttpAPI as a base class - the idea of Http
|
|
160
226
|
:returns: server response
|
161
227
|
"""
|
162
228
|
logging.debug('Reached %s.ping_dms', self.__class__.__name__)
|
163
|
-
return self.get([], headers=self.headers).content
|
164
229
|
|
230
|
+
return self.get([], headers=self.headers).content
|