|
a/scripts/allura_import.py |
|
b/scripts/allura_import.py |
|
... |
|
... |
7 |
import json
|
7 |
import json
|
8 |
from optparse import OptionParser
|
8 |
from optparse import OptionParser
|
9 |
from pprint import pprint
|
9 |
from pprint import pprint
|
10 |
from datetime import datetime
|
10 |
from datetime import datetime
|
11 |
|
11 |
|
|
|
12 |
from allura_api import AlluraApiClient
|
|
|
13 |
|
12 |
|
14 |
|
13 |
def parse_options():
|
15 |
def parse_options():
|
14 |
optparser = OptionParser(usage='''%prog [options] <JSON dump>
|
16 |
optparser = OptionParser(usage='''%prog [options] <JSON dump>
|
15 |
|
17 |
|
16 |
Import project data dump in JSON format into an Allura project.''')
|
18 |
Import project data dump in JSON format into an Allura project.''')
|
17 |
optparser.add_option('-a', '--api-key', dest='api_key', help='API key')
|
19 |
optparser.add_option('-a', '--api-ticket', dest='api_key', help='API ticket')
|
18 |
optparser.add_option('-s', '--secret-key', dest='secret_key', help='Secret key')
|
20 |
optparser.add_option('-s', '--secret-key', dest='secret_key', help='Secret key')
|
19 |
optparser.add_option('-p', '--project', dest='project', help='Project to import to')
|
21 |
optparser.add_option('-p', '--project', dest='project', help='Project to import to')
|
20 |
optparser.add_option('-t', '--tracker', dest='tracker', help='Tracker to import to')
|
22 |
optparser.add_option('-t', '--tracker', dest='tracker', help='Tracker to import to')
|
21 |
optparser.add_option('-u', '--base-url', dest='base_url', default='https://sourceforge.net', help='Base Allura URL (%default)')
|
23 |
optparser.add_option('-u', '--base-url', dest='base_url', default='https://sourceforge.net', help='Base Allura URL (%default)')
|
22 |
optparser.add_option('-o', dest='import_opts', default=[], action='append', help='Specify import option(s)', metavar='opt=val')
|
24 |
optparser.add_option('-o', dest='import_opts', default=[], action='append', help='Specify import option(s)', metavar='opt=val')
|
|
... |
|
... |
31 |
if not options.project or not options.tracker:
|
33 |
if not options.project or not options.tracker:
|
32 |
optparser.error("Target project and tracker are required")
|
34 |
optparser.error("Target project and tracker are required")
|
33 |
return optparser, options, args
|
35 |
return optparser, options, args
|
34 |
|
36 |
|
35 |
|
37 |
|
36 |
class AlluraRestClient(object):
|
|
|
37 |
|
|
|
38 |
def __init__(self, base_url, api_key, secret_key):
|
|
|
39 |
self.base_url = base_url
|
|
|
40 |
self.api_key = api_key
|
|
|
41 |
self.secret_key = secret_key
|
|
|
42 |
|
|
|
43 |
def sign(self, path, params):
|
|
|
44 |
params.append(('api_key', self.api_key))
|
|
|
45 |
params.append(('api_timestamp', datetime.utcnow().isoformat()))
|
|
|
46 |
message = path + '?' + urllib.urlencode(sorted(params))
|
|
|
47 |
digest = hmac.new(self.secret_key, message, hashlib.sha256).hexdigest()
|
|
|
48 |
params.append(('api_signature', digest))
|
|
|
49 |
return params
|
|
|
50 |
|
|
|
51 |
def call(self, url, **params):
|
|
|
52 |
url = urlparse.urljoin(options.base_url, url)
|
|
|
53 |
params = self.sign(urlparse.urlparse(url).path, params.items())
|
|
|
54 |
|
|
|
55 |
try:
|
|
|
56 |
result = urllib2.urlopen(url, urllib.urlencode(params))
|
|
|
57 |
return result.read()
|
|
|
58 |
except urllib2.HTTPError, e:
|
|
|
59 |
if options.verbose:
|
|
|
60 |
error_content = e.read()
|
|
|
61 |
e.msg += '. Error response:\n' + error_content
|
|
|
62 |
raise e
|
|
|
63 |
|
|
|
64 |
|
|
|
65 |
if __name__ == '__main__':
|
38 |
if __name__ == '__main__':
|
66 |
optparser, options, args = parse_options()
|
39 |
optparser, options, args = parse_options()
|
67 |
url = '/rest/p/' + options.project + '/' + options.tracker
|
|
|
68 |
if options.validate:
|
|
|
69 |
url += '/validate_import'
|
|
|
70 |
else:
|
|
|
71 |
url += '/perform_import'
|
|
|
72 |
|
40 |
|
73 |
import_options = {}
|
41 |
import_options = {}
|
74 |
for s in options.import_opts:
|
42 |
for s in options.import_opts:
|
75 |
k, v = s.split('=', 1)
|
43 |
k, v = s.split('=', 1)
|
76 |
if v == 'false':
|
44 |
if v == 'false':
|
|
... |
|
... |
93 |
finally:
|
61 |
finally:
|
94 |
f.close()
|
62 |
f.close()
|
95 |
|
63 |
|
96 |
import_options['user_map'] = user_map
|
64 |
import_options['user_map'] = user_map
|
97 |
|
65 |
|
98 |
cli = AlluraRestClient(options.base_url, options.api_key, options.secret_key)
|
66 |
cli = AlluraApiClient(options.base_url, options.api_key, options.secret_key, options.verbose)
|
|
|
67 |
url = '/rest/p/' + options.project + '/' + options.tracker
|
|
|
68 |
doc_txt = open(args[0]).read()
|
|
|
69 |
if options.validate:
|
|
|
70 |
url += '/validate_import'
|
99 |
print cli.call(url, doc=open(args[0]).read(), options=json.dumps(import_options))
|
71 |
print cli.call(url, doc=doc_txt, options=json.dumps(import_options))
|
|
|
72 |
else:
|
|
|
73 |
url += '/perform_import'
|
|
|
74 |
|
|
|
75 |
doc = json.loads(doc_txt)
|
|
|
76 |
tickets_in = doc['trackers']['default']['artifacts']
|
|
|
77 |
doc['trackers']['default']['artifacts'] = []
|
|
|
78 |
if options.verbose:
|
|
|
79 |
print "Importing %d tickets" % len(tickets_in)
|
|
|
80 |
|
|
|
81 |
cnt = 0
|
|
|
82 |
for ticket_in in tickets_in:
|
|
|
83 |
cnt += 1
|
|
|
84 |
doc['trackers']['default']['artifacts'] = [ticket_in]
|
|
|
85 |
res = cli.call(url, doc=json.dumps(doc), options=json.dumps(import_options))
|
|
|
86 |
assert res['status'] and not res['errors']
|
|
|
87 |
if res['warnings']:
|
|
|
88 |
print "Imported ticket id %s, warnings: %s" % (ticket_in['id'], res['warnings'])
|
|
|
89 |
else:
|
|
|
90 |
print "Imported ticket id %s" % (ticket_in['id'])
|