Show More
@@ -0,0 +1,148 b'' | |||||
|
1 | """ | |||
|
2 | Base utils for shell scripts | |||
|
3 | """ | |||
|
4 | import os | |||
|
5 | import sys | |||
|
6 | import random | |||
|
7 | import urllib2 | |||
|
8 | import pprint | |||
|
9 | ||||
|
10 | try: | |||
|
11 | from rhodecode.lib.ext_json import json | |||
|
12 | except ImportError: | |||
|
13 | try: | |||
|
14 | import simplejson as json | |||
|
15 | except ImportError: | |||
|
16 | import json | |||
|
17 | ||||
|
18 | CONFIG_NAME = '.rhodecode' | |||
|
19 | FORMAT_PRETTY = 'pretty' | |||
|
20 | FORMAT_JSON = 'json' | |||
|
21 | ||||
|
22 | ||||
|
23 | def api_call(apikey, apihost, format, method=None, **kw): | |||
|
24 | """ | |||
|
25 | Api_call wrapper for RhodeCode | |||
|
26 | ||||
|
27 | :param apikey: | |||
|
28 | :param apihost: | |||
|
29 | :param format: formatting, pretty means prints and pprint of json | |||
|
30 | json returns unparsed json | |||
|
31 | :param method: | |||
|
32 | """ | |||
|
33 | def _build_data(random_id): | |||
|
34 | """ | |||
|
35 | Builds API data with given random ID | |||
|
36 | ||||
|
37 | :param random_id: | |||
|
38 | :type random_id: | |||
|
39 | """ | |||
|
40 | return { | |||
|
41 | "id": random_id, | |||
|
42 | "api_key": apikey, | |||
|
43 | "method": method, | |||
|
44 | "args": kw | |||
|
45 | } | |||
|
46 | ||||
|
47 | if not method: | |||
|
48 | raise Exception('please specify method name !') | |||
|
49 | id_ = random.randrange(1, 9999) | |||
|
50 | req = urllib2.Request('%s/_admin/api' % apihost, | |||
|
51 | data=json.dumps(_build_data(id_)), | |||
|
52 | headers={'content-type': 'text/plain'}) | |||
|
53 | if format == FORMAT_PRETTY: | |||
|
54 | sys.stdout.write('calling %s to %s \n' % (req.get_data(), apihost)) | |||
|
55 | ret = urllib2.urlopen(req) | |||
|
56 | raw_json = ret.read() | |||
|
57 | json_data = json.loads(raw_json) | |||
|
58 | id_ret = json_data['id'] | |||
|
59 | _formatted_json = pprint.pformat(json_data) | |||
|
60 | if id_ret == id_: | |||
|
61 | if format == FORMAT_JSON: | |||
|
62 | sys.stdout.write(str(raw_json)) | |||
|
63 | else: | |||
|
64 | sys.stdout.write('rhodecode returned:\n%s\n' % (_formatted_json)) | |||
|
65 | ||||
|
66 | else: | |||
|
67 | raise Exception('something went wrong. ' | |||
|
68 | 'ID mismatch got %s, expected %s | %s' % ( | |||
|
69 | id_ret, id_, _formatted_json)) | |||
|
70 | ||||
|
71 | ||||
|
72 | class RcConf(object): | |||
|
73 | """ | |||
|
74 | RhodeCode config for API | |||
|
75 | ||||
|
76 | conf = RcConf() | |||
|
77 | conf['key'] | |||
|
78 | ||||
|
79 | """ | |||
|
80 | ||||
|
81 | def __init__(self, config_location=None, autoload=True, autocreate=False, | |||
|
82 | config=None): | |||
|
83 | self._conf_name = CONFIG_NAME if not config_location else config_location | |||
|
84 | self._conf = {} | |||
|
85 | if autocreate: | |||
|
86 | self.make_config(config) | |||
|
87 | if autoload: | |||
|
88 | self._conf = self.load_config() | |||
|
89 | ||||
|
90 | def __getitem__(self, key): | |||
|
91 | return self._conf[key] | |||
|
92 | ||||
|
93 | def __nonzero__(self): | |||
|
94 | if self._conf: | |||
|
95 | return True | |||
|
96 | return False | |||
|
97 | ||||
|
98 | def __eq__(self): | |||
|
99 | return self._conf.__eq__() | |||
|
100 | ||||
|
101 | def __repr__(self): | |||
|
102 | return 'RcConf<%s>' % self._conf.__repr__() | |||
|
103 | ||||
|
104 | def make_config(self, config): | |||
|
105 | """ | |||
|
106 | Saves given config as a JSON dump in the _conf_name location | |||
|
107 | ||||
|
108 | :param config: | |||
|
109 | :type config: | |||
|
110 | """ | |||
|
111 | update = False | |||
|
112 | if os.path.exists(self._conf_name): | |||
|
113 | update = True | |||
|
114 | with open(self._conf_name, 'wb') as f: | |||
|
115 | json.dump(config, f, indent=4) | |||
|
116 | ||||
|
117 | if update: | |||
|
118 | sys.stdout.write('Updated config in %s\n' % self._conf_name) | |||
|
119 | else: | |||
|
120 | sys.stdout.write('Created new config in %s\n' % self._conf_name) | |||
|
121 | ||||
|
122 | def update_config(self, new_config): | |||
|
123 | """ | |||
|
124 | Reads the JSON config updates it's values with new_config and | |||
|
125 | saves it back as JSON dump | |||
|
126 | ||||
|
127 | :param new_config: | |||
|
128 | """ | |||
|
129 | config = {} | |||
|
130 | try: | |||
|
131 | with open(self._conf_name, 'rb') as conf: | |||
|
132 | config = json.load(conf) | |||
|
133 | except IOError, e: | |||
|
134 | sys.stderr.write(str(e) + '\n') | |||
|
135 | ||||
|
136 | config.update(new_config) | |||
|
137 | self.make_config(config) | |||
|
138 | ||||
|
139 | def load_config(self): | |||
|
140 | """ | |||
|
141 | Loads config from file and returns loaded JSON object | |||
|
142 | """ | |||
|
143 | try: | |||
|
144 | with open(self._conf_name, 'rb') as conf: | |||
|
145 | return json.load(conf) | |||
|
146 | except IOError, e: | |||
|
147 | #sys.stderr.write(str(e) + '\n') | |||
|
148 | pass |
@@ -1,249 +1,107 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | """ |
|
2 | """ | |
3 |
rhodecode.bin. |
|
3 | rhodecode.bin.api | |
4 |
~~~~~~~~~~~~~~~~~ |
|
4 | ~~~~~~~~~~~~~~~~~ | |
5 |
|
5 | |||
6 | Api CLI client for RhodeCode |
|
6 | Api CLI client for RhodeCode | |
7 |
|
7 | |||
8 | :created_on: Jun 3, 2012 |
|
8 | :created_on: Jun 3, 2012 | |
9 | :author: marcink |
|
9 | :author: marcink | |
10 | :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com> |
|
10 | :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com> | |
11 | :license: GPLv3, see COPYING for more details. |
|
11 | :license: GPLv3, see COPYING for more details. | |
12 | """ |
|
12 | """ | |
13 | # This program is free software: you can redistribute it and/or modify |
|
13 | # This program is free software: you can redistribute it and/or modify | |
14 | # it under the terms of the GNU General Public License as published by |
|
14 | # it under the terms of the GNU General Public License as published by | |
15 | # the Free Software Foundation, either version 3 of the License, or |
|
15 | # the Free Software Foundation, either version 3 of the License, or | |
16 | # (at your option) any later version. |
|
16 | # (at your option) any later version. | |
17 | # |
|
17 | # | |
18 | # This program is distributed in the hope that it will be useful, |
|
18 | # This program is distributed in the hope that it will be useful, | |
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | # GNU General Public License for more details. |
|
21 | # GNU General Public License for more details. | |
22 | # |
|
22 | # | |
23 | # You should have received a copy of the GNU General Public License |
|
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
24 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
25 |
|
25 | |||
26 | from __future__ import with_statement |
|
26 | from __future__ import with_statement | |
27 | import os |
|
|||
28 | import sys |
|
27 | import sys | |
29 | import random |
|
|||
30 | import urllib2 |
|
|||
31 | import pprint |
|
|||
32 | import argparse |
|
28 | import argparse | |
33 |
|
29 | |||
34 | try: |
|
30 | from rhodecode.bin.base import api_call, RcConf, FORMAT_JSON, FORMAT_PRETTY | |
35 | from rhodecode.lib.ext_json import json |
|
|||
36 | except ImportError: |
|
|||
37 | try: |
|
|||
38 | import simplejson as json |
|
|||
39 | except ImportError: |
|
|||
40 | import json |
|
|||
41 |
|
||||
42 |
|
||||
43 | CONFIG_NAME = '.rhodecode' |
|
|||
44 | FORMAT_PRETTY = 'pretty' |
|
|||
45 | FORMAT_JSON = 'json' |
|
|||
46 |
|
||||
47 |
|
||||
48 | class RcConf(object): |
|
|||
49 | """ |
|
|||
50 | RhodeCode config for API |
|
|||
51 |
|
||||
52 | conf = RcConf() |
|
|||
53 | conf['key'] |
|
|||
54 |
|
||||
55 | """ |
|
|||
56 |
|
||||
57 | def __init__(self, config_location=None, autoload=True, autocreate=False, |
|
|||
58 | config=None): |
|
|||
59 | self._conf_name = CONFIG_NAME if not config_location else config_location |
|
|||
60 | self._conf = {} |
|
|||
61 | if autocreate: |
|
|||
62 | self.make_config(config) |
|
|||
63 | if autoload: |
|
|||
64 | self._conf = self.load_config() |
|
|||
65 |
|
||||
66 | def __getitem__(self, key): |
|
|||
67 | return self._conf[key] |
|
|||
68 |
|
||||
69 | def __nonzero__(self): |
|
|||
70 | if self._conf: |
|
|||
71 | return True |
|
|||
72 | return False |
|
|||
73 |
|
||||
74 | def __eq__(self): |
|
|||
75 | return self._conf.__eq__() |
|
|||
76 |
|
||||
77 | def __repr__(self): |
|
|||
78 | return 'RcConf<%s>' % self._conf.__repr__() |
|
|||
79 |
|
||||
80 | def make_config(self, config): |
|
|||
81 | """ |
|
|||
82 | Saves given config as a JSON dump in the _conf_name location |
|
|||
83 |
|
||||
84 | :param config: |
|
|||
85 | :type config: |
|
|||
86 | """ |
|
|||
87 | update = False |
|
|||
88 | if os.path.exists(self._conf_name): |
|
|||
89 | update = True |
|
|||
90 | with open(self._conf_name, 'wb') as f: |
|
|||
91 | json.dump(config, f, indent=4) |
|
|||
92 |
|
||||
93 | if update: |
|
|||
94 | sys.stdout.write('Updated config in %s\n' % self._conf_name) |
|
|||
95 | else: |
|
|||
96 | sys.stdout.write('Created new config in %s\n' % self._conf_name) |
|
|||
97 |
|
||||
98 | def update_config(self, new_config): |
|
|||
99 | """ |
|
|||
100 | Reads the JSON config updates it's values with new_config and |
|
|||
101 | saves it back as JSON dump |
|
|||
102 |
|
||||
103 | :param new_config: |
|
|||
104 | """ |
|
|||
105 | config = {} |
|
|||
106 | try: |
|
|||
107 | with open(self._conf_name, 'rb') as conf: |
|
|||
108 | config = json.load(conf) |
|
|||
109 | except IOError, e: |
|
|||
110 | sys.stderr.write(str(e) + '\n') |
|
|||
111 |
|
||||
112 | config.update(new_config) |
|
|||
113 | self.make_config(config) |
|
|||
114 |
|
||||
115 | def load_config(self): |
|
|||
116 | """ |
|
|||
117 | Loads config from file and returns loaded JSON object |
|
|||
118 | """ |
|
|||
119 | try: |
|
|||
120 | with open(self._conf_name, 'rb') as conf: |
|
|||
121 | return json.load(conf) |
|
|||
122 | except IOError, e: |
|
|||
123 | #sys.stderr.write(str(e) + '\n') |
|
|||
124 | pass |
|
|||
125 |
|
||||
126 |
|
||||
127 | def api_call(apikey, apihost, format, method=None, **kw): |
|
|||
128 | """ |
|
|||
129 | Api_call wrapper for RhodeCode |
|
|||
130 |
|
||||
131 | :param apikey: |
|
|||
132 | :param apihost: |
|
|||
133 | :param format: formatting, pretty means prints and pprint of json |
|
|||
134 | json returns unparsed json |
|
|||
135 | :param method: |
|
|||
136 | """ |
|
|||
137 | def _build_data(random_id): |
|
|||
138 | """ |
|
|||
139 | Builds API data with given random ID |
|
|||
140 |
|
||||
141 | :param random_id: |
|
|||
142 | :type random_id: |
|
|||
143 | """ |
|
|||
144 | return { |
|
|||
145 | "id": random_id, |
|
|||
146 | "api_key": apikey, |
|
|||
147 | "method": method, |
|
|||
148 | "args": kw |
|
|||
149 | } |
|
|||
150 |
|
||||
151 | if not method: |
|
|||
152 | raise Exception('please specify method name !') |
|
|||
153 | id_ = random.randrange(1, 9999) |
|
|||
154 | req = urllib2.Request('%s/_admin/api' % apihost, |
|
|||
155 | data=json.dumps(_build_data(id_)), |
|
|||
156 | headers={'content-type': 'text/plain'}) |
|
|||
157 | if format == FORMAT_PRETTY: |
|
|||
158 | sys.stdout.write('calling %s to %s \n' % (req.get_data(), apihost)) |
|
|||
159 | ret = urllib2.urlopen(req) |
|
|||
160 | raw_json = ret.read() |
|
|||
161 | json_data = json.loads(raw_json) |
|
|||
162 | id_ret = json_data['id'] |
|
|||
163 | _formatted_json = pprint.pformat(json_data) |
|
|||
164 | if id_ret == id_: |
|
|||
165 | if format == FORMAT_JSON: |
|
|||
166 | sys.stdout.write(str(raw_json)) |
|
|||
167 | else: |
|
|||
168 | sys.stdout.write('rhodecode returned:\n%s\n' % (_formatted_json)) |
|
|||
169 |
|
||||
170 | else: |
|
|||
171 | raise Exception('something went wrong. ' |
|
|||
172 | 'ID mismatch got %s, expected %s | %s' % ( |
|
|||
173 | id_ret, id_, _formatted_json)) |
|
|||
174 |
|
31 | |||
175 |
|
32 | |||
176 | def argparser(argv): |
|
33 | def argparser(argv): | |
177 | usage = ( |
|
34 | usage = ( | |
178 |
"rhodecode |
|
35 | "rhodecode-api [-h] [--format=FORMAT] [--apikey=APIKEY] [--apihost=APIHOST] " | |
179 |
" |
|
36 | "[--config=CONFIG] [--save-config] " | |
180 |
" |
|
37 | "METHOD <key:val> <key2:val> ...\n" | |
|
38 | "Create config file: rhodecode-gist --apikey=<key> --apihost=http://rhodecode.server --save-config" | |||
181 | ) |
|
39 | ) | |
182 |
|
40 | |||
183 | parser = argparse.ArgumentParser(description='RhodeCode API cli', |
|
41 | parser = argparse.ArgumentParser(description='RhodeCode API cli', | |
184 | usage=usage) |
|
42 | usage=usage) | |
185 |
|
43 | |||
186 | ## config |
|
44 | ## config | |
187 | group = parser.add_argument_group('config') |
|
45 | group = parser.add_argument_group('config') | |
188 | group.add_argument('--apikey', help='api access key') |
|
46 | group.add_argument('--apikey', help='api access key') | |
189 | group.add_argument('--apihost', help='api host') |
|
47 | group.add_argument('--apihost', help='api host') | |
190 | group.add_argument('--config', help='config file') |
|
48 | group.add_argument('--config', help='config file') | |
|
49 | group.add_argument('--save-config', action='store_true', help='save the given config into a file') | |||
191 |
|
50 | |||
192 | group = parser.add_argument_group('API') |
|
51 | group = parser.add_argument_group('API') | |
193 | group.add_argument('method', metavar='METHOD', type=str, |
|
52 | group.add_argument('method', metavar='METHOD', nargs='?', type=str, default=None, | |
194 | help='API method name to call followed by key:value attributes', |
|
53 | help='API method name to call followed by key:value attributes', | |
195 | ) |
|
54 | ) | |
196 | group.add_argument('--format', dest='format', type=str, |
|
55 | group.add_argument('--format', dest='format', type=str, | |
197 | help='output format default: `pretty` can ' |
|
56 | help='output format default: `pretty` can ' | |
198 | 'be also `%s`' % FORMAT_JSON, |
|
57 | 'be also `%s`' % FORMAT_JSON, | |
199 | default=FORMAT_PRETTY |
|
58 | default=FORMAT_PRETTY | |
200 | ) |
|
59 | ) | |
201 | args, other = parser.parse_known_args() |
|
60 | args, other = parser.parse_known_args() | |
202 | return parser, args, other |
|
61 | return parser, args, other | |
203 |
|
62 | |||
204 |
|
63 | |||
205 | def main(argv=None): |
|
64 | def main(argv=None): | |
206 | """ |
|
65 | """ | |
207 | Main execution function for cli |
|
66 | Main execution function for cli | |
208 |
|
67 | |||
209 | :param argv: |
|
68 | :param argv: | |
210 | :type argv: |
|
69 | :type argv: | |
211 | """ |
|
70 | """ | |
212 | if argv is None: |
|
71 | if argv is None: | |
213 | argv = sys.argv |
|
72 | argv = sys.argv | |
214 |
|
73 | |||
215 | conf = None |
|
74 | conf = None | |
216 | parser, args, other = argparser(argv) |
|
75 | parser, args, other = argparser(argv) | |
217 |
|
76 | |||
218 | api_credentials_given = (args.apikey and args.apihost) |
|
77 | api_credentials_given = (args.apikey and args.apihost) | |
219 |
if args. |
|
78 | if args.save_config: | |
220 | if not api_credentials_given: |
|
79 | if not api_credentials_given: | |
221 |
raise parser.error(' |
|
80 | raise parser.error('--save-config requires --apikey and --apihost') | |
222 | conf = RcConf(config_location=args.config, |
|
81 | conf = RcConf(config_location=args.config, | |
223 | autocreate=True, config={'apikey': args.apikey, |
|
82 | autocreate=True, config={'apikey': args.apikey, | |
224 | 'apihost': args.apihost}) |
|
83 | 'apihost': args.apihost}) | |
|
84 | sys.exit() | |||
225 |
|
85 | |||
226 | if not conf: |
|
86 | if not conf: | |
227 | conf = RcConf(config_location=args.config, autoload=True) |
|
87 | conf = RcConf(config_location=args.config, autoload=True) | |
228 | if not conf: |
|
88 | if not conf: | |
229 | if not api_credentials_given: |
|
89 | if not api_credentials_given: | |
230 | parser.error('Could not find config file and missing ' |
|
90 | parser.error('Could not find config file and missing ' | |
231 | '--apikey or --apihost in params') |
|
91 | '--apikey or --apihost in params') | |
232 |
|
92 | |||
233 | apikey = args.apikey or conf['apikey'] |
|
93 | apikey = args.apikey or conf['apikey'] | |
234 | host = args.apihost or conf['apihost'] |
|
94 | host = args.apihost or conf['apihost'] | |
235 | method = args.method |
|
95 | method = args.method | |
236 | if method == '_create_config': |
|
|||
237 | sys.exit() |
|
|||
238 |
|
96 | |||
239 | try: |
|
97 | try: | |
240 | margs = dict(map(lambda s: s.split(':', 1), other)) |
|
98 | margs = dict(map(lambda s: s.split(':', 1), other)) | |
241 | except Exception: |
|
99 | except Exception: | |
242 | sys.stderr.write('Error parsing arguments \n') |
|
100 | sys.stderr.write('Error parsing arguments \n') | |
243 | sys.exit() |
|
101 | sys.exit() | |
244 |
|
102 | |||
245 | api_call(apikey, host, args.format, method, **margs) |
|
103 | api_call(apikey, host, args.format, method, **margs) | |
246 | return 0 |
|
104 | return 0 | |
247 |
|
105 | |||
248 | if __name__ == '__main__': |
|
106 | if __name__ == '__main__': | |
249 | sys.exit(main(sys.argv)) |
|
107 | sys.exit(main(sys.argv)) |
General Comments 0
You need to be logged in to leave comments.
Login now