Package coprs :: Package views :: Package api_ns :: Module api_general
[hide private]
[frames] | no frames]

Source Code for Module coprs.views.api_ns.api_general

  1  import base64 
  2  import datetime 
  3  from functools import wraps 
  4  import json 
  5  import os 
  6  import flask 
  7  import sqlalchemy 
  8   
  9  from werkzeug import secure_filename 
 10   
 11  from coprs import db 
 12  from coprs import exceptions 
 13  from coprs import forms 
 14  from coprs import helpers 
 15  from coprs import models 
 16  from coprs.helpers import fix_protocol_for_backend, generate_build_config 
 17  from coprs.logic.api_logic import MonitorWrapper 
 18  from coprs.logic.builds_logic import BuildsLogic 
 19  from coprs.logic.complex_logic import ComplexLogic 
 20  from coprs.logic.users_logic import UsersLogic 
 21  from coprs.logic.packages_logic import PackagesLogic 
 22  from coprs.logic.modules_logic import ModulesLogic 
 23   
 24  from coprs.views.misc import login_required, api_login_required 
 25   
 26  from coprs.views.api_ns import api_ns 
 27   
 28  from coprs.logic import builds_logic 
 29  from coprs.logic import coprs_logic 
 30  from coprs.logic.coprs_logic import CoprsLogic 
 31  from coprs.logic.actions_logic import ActionsLogic 
 32   
 33  from coprs.exceptions import (ActionInProgressException, 
 34                                InsufficientRightsException, 
 35                                DuplicateException, 
 36                                LegacyApiError, 
 37                                UnknownSourceTypeException) 
38 39 40 -def api_req_with_copr(f):
41 @wraps(f) 42 def wrapper(username, coprname, **kwargs): 43 if username.startswith("@"): 44 group_name = username[1:] 45 copr = ComplexLogic.get_group_copr_safe(group_name, coprname) 46 else: 47 copr = ComplexLogic.get_copr_safe(username, coprname) 48 49 return f(copr, **kwargs)
50 return wrapper 51
52 53 @api_ns.route("/") 54 -def api_home():
55 """ 56 Render the home page of the api. 57 This page provides information on how to call/use the API. 58 """ 59 60 return flask.render_template("api.html")
61 62 63 @api_ns.route("/new/", methods=["GET", "POST"])
64 @login_required 65 -def api_new_token():
66 """ 67 Generate a new API token for the current user. 68 """ 69 70 user = flask.g.user 71 copr64 = base64.b64encode(b"copr") + b"##" 72 api_login = helpers.generate_api_token( 73 flask.current_app.config["API_TOKEN_LENGTH"] - len(copr64)) 74 user.api_login = api_login 75 user.api_token = helpers.generate_api_token( 76 flask.current_app.config["API_TOKEN_LENGTH"]) 77 user.api_token_expiration = datetime.date.today() + \ 78 datetime.timedelta( 79 days=flask.current_app.config["API_TOKEN_EXPIRATION"]) 80 81 db.session.add(user) 82 db.session.commit() 83 return flask.redirect(flask.url_for("api_ns.api_home"))
84 85 86 @api_ns.route("/coprs/<username>/new/", methods=["POST"])
87 @api_login_required 88 -def api_new_copr(username):
89 """ 90 Receive information from the user on how to create its new copr, 91 check their validity and create the corresponding copr. 92 93 :arg name: the name of the copr to add 94 :arg chroots: a comma separated list of chroots to use 95 :kwarg repos: a comma separated list of repository that this copr 96 can use. 97 :kwarg initial_pkgs: a comma separated list of initial packages to 98 build in this new copr 99 100 """ 101 102 form = forms.CoprFormFactory.create_form_cls()(csrf_enabled=False) 103 infos = [] 104 105 # are there any arguments in POST which our form doesn't know? 106 # TODO: don't use WTFform for parsing and validation here 107 for post_key in flask.request.form.keys(): 108 if post_key not in form.__dict__.keys(): 109 infos.append("Unknown key '{key}' received.".format(key=post_key)) 110 111 if form.validate_on_submit(): 112 group = ComplexLogic.get_group_by_name_safe(username[1:]) if username[0] == "@" else None 113 114 auto_prune = True 115 if "auto_prune" in flask.request.form: 116 auto_prune = form.auto_prune.data 117 118 try: 119 copr = CoprsLogic.add( 120 name=form.name.data.strip(), 121 repos=" ".join(form.repos.data.split()), 122 user=flask.g.user, 123 selected_chroots=form.selected_chroots, 124 description=form.description.data, 125 instructions=form.instructions.data, 126 check_for_duplicates=True, 127 disable_createrepo=form.disable_createrepo.data, 128 unlisted_on_hp=form.unlisted_on_hp.data, 129 build_enable_net=form.build_enable_net.data, 130 group=group, 131 persistent=form.persistent.data, 132 auto_prune=auto_prune, 133 ) 134 infos.append("New project was successfully created.") 135 136 if form.initial_pkgs.data: 137 pkgs = form.initial_pkgs.data.split() 138 for pkg in pkgs: 139 builds_logic.BuildsLogic.add( 140 user=flask.g.user, 141 pkgs=pkg, 142 copr=copr) 143 144 infos.append("Initial packages were successfully " 145 "submitted for building.") 146 147 output = {"output": "ok", "message": "\n".join(infos)} 148 db.session.commit() 149 except (exceptions.DuplicateException, 150 exceptions.NonAdminCannotCreatePersistentProject, 151 exceptions.NonAdminCannotDisableAutoPrunning) as err: 152 db.session.rollback() 153 raise LegacyApiError(str(err)) 154 155 else: 156 errormsg = "Validation error\n" 157 if form.errors: 158 for field, emsgs in form.errors.items(): 159 errormsg += "- {0}: {1}\n".format(field, "\n".join(emsgs)) 160 161 errormsg = errormsg.replace('"', "'") 162 raise LegacyApiError(errormsg) 163 164 return flask.jsonify(output)
165 166 167 @api_ns.route("/coprs/<username>/<coprname>/delete/", methods=["POST"])
168 @api_login_required 169 @api_req_with_copr 170 -def api_copr_delete(copr):
171 """ Deletes selected user's project 172 """ 173 form = forms.CoprDeleteForm(csrf_enabled=False) 174 httpcode = 200 175 176 if form.validate_on_submit() and copr: 177 try: 178 ComplexLogic.delete_copr(copr) 179 except (exceptions.ActionInProgressException, 180 exceptions.InsufficientRightsException) as err: 181 182 db.session.rollback() 183 raise LegacyApiError(str(err)) 184 else: 185 message = "Project {} has been deleted.".format(copr.name) 186 output = {"output": "ok", "message": message} 187 db.session.commit() 188 else: 189 raise LegacyApiError("Invalid request: {0}".format(form.errors)) 190 191 return flask.jsonify(output)
192 193 194 @api_ns.route("/coprs/<username>/<coprname>/fork/", methods=["POST"])
195 @api_login_required 196 @api_req_with_copr 197 -def api_copr_fork(copr):
198 """ Fork the project and builds in it 199 """ 200 form = forms.CoprForkFormFactory\ 201 .create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)(csrf_enabled=False) 202 203 if form.validate_on_submit() and copr: 204 try: 205 dstgroup = ([g for g in flask.g.user.user_groups if g.at_name == form.owner.data] or [None])[0] 206 if flask.g.user.name != form.owner.data and not dstgroup: 207 return LegacyApiError("There is no such group: {}".format(form.owner.data)) 208 209 fcopr, created = ComplexLogic.fork_copr(copr, flask.g.user, dstname=form.name.data, dstgroup=dstgroup) 210 if created: 211 msg = ("Forking project {} for you into {}.\nPlease be aware that it may take a few minutes " 212 "to duplicate a backend data.".format(copr.full_name, fcopr.full_name)) 213 elif not created and form.confirm.data == True: 214 msg = ("Updating packages in {} from {}.\nPlease be aware that it may take a few minutes " 215 "to duplicate a backend data.".format(copr.full_name, fcopr.full_name)) 216 else: 217 raise LegacyApiError("You are about to fork into existing project: {}\n" 218 "Please use --confirm if you really want to do this".format(fcopr.full_name)) 219 220 output = {"output": "ok", "message": msg} 221 db.session.commit() 222 223 except (exceptions.ActionInProgressException, 224 exceptions.InsufficientRightsException) as err: 225 db.session.rollback() 226 raise LegacyApiError(str(err)) 227 else: 228 raise LegacyApiError("Invalid request: {0}".format(form.errors)) 229 230 return flask.jsonify(output)
231
232 233 @api_ns.route("/coprs/") 234 @api_ns.route("/coprs/<username>/") 235 -def api_coprs_by_owner(username=None):
236 """ Return the list of coprs owned by the given user. 237 username is taken either from GET params or from the URL itself 238 (in this order). 239 240 :arg username: the username of the person one would like to the 241 coprs of. 242 243 """ 244 username = flask.request.args.get("username", None) or username 245 if username is None: 246 raise LegacyApiError("Invalid request: missing `username` ") 247 248 release_tmpl = "{chroot.os_release}-{chroot.os_version}-{chroot.arch}" 249 250 if username.startswith("@"): 251 group_name = username[1:] 252 query = CoprsLogic.get_multiple() 253 query = CoprsLogic.filter_by_group_name(query, group_name) 254 else: 255 query = CoprsLogic.get_multiple_owned_by_username(username) 256 257 query = CoprsLogic.join_builds(query) 258 query = CoprsLogic.set_query_order(query) 259 260 repos = query.all() 261 output = {"output": "ok", "repos": []} 262 for repo in repos: 263 yum_repos = {} 264 for build in repo.builds: 265 if build.results: 266 for chroot in repo.active_chroots: 267 release = release_tmpl.format(chroot=chroot) 268 yum_repos[release] = fix_protocol_for_backend( 269 os.path.join(build.results, release + '/')) 270 break 271 272 output["repos"].append({"name": repo.name, 273 "additional_repos": repo.repos, 274 "yum_repos": yum_repos, 275 "description": repo.description, 276 "instructions": repo.instructions, 277 "persistent": repo.persistent, 278 "unlisted_on_hp": repo.unlisted_on_hp, 279 "auto_prune": repo.auto_prune, 280 }) 281 282 return flask.jsonify(output)
283
284 285 @api_ns.route("/coprs/<username>/<coprname>/detail/") 286 @api_req_with_copr 287 -def api_coprs_by_owner_detail(copr):
288 """ Return detail of one project. 289 290 :arg username: the username of the person one would like to the 291 coprs of. 292 :arg coprname: the name of project. 293 294 """ 295 release_tmpl = "{chroot.os_release}-{chroot.os_version}-{chroot.arch}" 296 output = {"output": "ok", "detail": {}} 297 yum_repos = {} 298 299 build = models.Build.query.filter( 300 models.Build.copr_id == copr.id, models.Build.results != None).first() 301 302 if build: 303 for chroot in copr.active_chroots: 304 release = release_tmpl.format(chroot=chroot) 305 yum_repos[release] = fix_protocol_for_backend( 306 os.path.join(build.results, release + '/')) 307 308 output["detail"] = { 309 "name": copr.name, 310 "additional_repos": copr.repos, 311 "yum_repos": yum_repos, 312 "description": copr.description, 313 "instructions": copr.instructions, 314 "last_modified": builds_logic.BuildsLogic.last_modified(copr), 315 "auto_createrepo": copr.auto_createrepo, 316 "persistent": copr.persistent, 317 "unlisted_on_hp": copr.unlisted_on_hp, 318 "auto_prune": copr.auto_prune, 319 } 320 return flask.jsonify(output)
321 322 323 @api_ns.route("/coprs/<username>/<coprname>/new_build/", methods=["POST"])
324 @api_login_required 325 @api_req_with_copr 326 -def copr_new_build(copr):
327 form = forms.BuildFormUrlFactory(copr.active_chroots)(csrf_enabled=False) 328 329 def create_new_build(): 330 # create separate build for each package 331 pkgs = form.pkgs.data.split("\n") 332 return [BuildsLogic.create_new_from_url( 333 flask.g.user, copr, 334 srpm_url=pkg, 335 chroot_names=form.selected_chroots, 336 background=form.background.data, 337 ) for pkg in pkgs]
338 return process_creating_new_build(copr, form, create_new_build) 339 340 341 @api_ns.route("/coprs/<username>/<coprname>/new_build_upload/", methods=["POST"])
342 @api_login_required 343 @api_req_with_copr 344 -def copr_new_build_upload(copr):
345 form = forms.BuildFormUploadFactory(copr.active_chroots)(csrf_enabled=False) 346 347 def create_new_build(): 348 return BuildsLogic.create_new_from_upload( 349 flask.g.user, copr, 350 f_uploader=lambda path: form.pkgs.data.save(path), 351 orig_filename=secure_filename(form.pkgs.data.filename), 352 chroot_names=form.selected_chroots, 353 background=form.background.data, 354 )
355 return process_creating_new_build(copr, form, create_new_build) 356 357 358 @api_ns.route("/coprs/<username>/<coprname>/new_build_pypi/", methods=["POST"])
359 @api_login_required 360 @api_req_with_copr 361 -def copr_new_build_pypi(copr):
362 form = forms.BuildFormPyPIFactory(copr.active_chroots)(csrf_enabled=False) 363 364 # TODO: automatically prepopulate all form fields with their defaults 365 if not form.python_versions.data: 366 form.python_versions.data = form.python_versions.default 367 368 def create_new_build(): 369 return BuildsLogic.create_new_from_pypi( 370 flask.g.user, 371 copr, 372 form.pypi_package_name.data, 373 form.pypi_package_version.data, 374 form.python_versions.data, 375 form.selected_chroots, 376 background=form.background.data, 377 )
378 return process_creating_new_build(copr, form, create_new_build) 379 380 381 @api_ns.route("/coprs/<username>/<coprname>/new_build_tito/", methods=["POST"])
382 @api_login_required 383 @api_req_with_copr 384 -def copr_new_build_tito(copr):
385 form = forms.BuildFormTitoFactory(copr.active_chroots)(csrf_enabled=False) 386 387 def create_new_build(): 388 return BuildsLogic.create_new_from_tito( 389 flask.g.user, 390 copr, 391 form.git_url.data, 392 form.git_directory.data, 393 form.git_branch.data, 394 form.tito_test.data, 395 form.selected_chroots, 396 background=form.background.data, 397 )
398 return process_creating_new_build(copr, form, create_new_build) 399 400 401 @api_ns.route("/coprs/<username>/<coprname>/new_build_mock/", methods=["POST"])
402 @api_login_required 403 @api_req_with_copr 404 -def copr_new_build_mock(copr):
405 form = forms.BuildFormMockFactory(copr.active_chroots)(csrf_enabled=False) 406 407 def create_new_build(): 408 return BuildsLogic.create_new_from_mock( 409 flask.g.user, 410 copr, 411 form.scm_type.data, 412 form.scm_url.data, 413 form.scm_branch.data, 414 form.spec.data, 415 form.selected_chroots, 416 background=form.background.data, 417 )
418 return process_creating_new_build(copr, form, create_new_build) 419 420 421 @api_ns.route("/coprs/<username>/<coprname>/new_build_rubygems/", methods=["POST"])
422 @api_login_required 423 @api_req_with_copr 424 -def copr_new_build_rubygems(copr):
425 form = forms.BuildFormRubyGemsFactory(copr.active_chroots)(csrf_enabled=False) 426 427 def create_new_build(): 428 return BuildsLogic.create_new_from_rubygems( 429 flask.g.user, 430 copr, 431 form.gem_name.data, 432 form.selected_chroots, 433 background=form.background.data, 434 )
435 return process_creating_new_build(copr, form, create_new_build) 436 437 438 @api_ns.route("/coprs/<username>/<coprname>/new_build_distgit/", methods=["POST"])
439 @api_login_required 440 @api_req_with_copr 441 -def copr_new_build_distgit(copr):
442 form = forms.BuildFormDistGitFactory(copr.active_chroots)(csrf_enabled=False) 443 444 def create_new_build(): 445 return BuildsLogic.create_new_from_distgit( 446 flask.g.user, 447 copr, 448 form.clone_url.data, 449 form.branch.data, 450 form.selected_chroots, 451 background=form.background.data, 452 )
453 return process_creating_new_build(copr, form, create_new_build) 454
455 456 -def process_creating_new_build(copr, form, create_new_build):
457 infos = [] 458 459 # are there any arguments in POST which our form doesn't know? 460 for post_key in flask.request.form.keys(): 461 if post_key not in form.__dict__.keys(): 462 infos.append("Unknown key '{key}' received.".format(key=post_key)) 463 464 if not form.validate_on_submit(): 465 raise LegacyApiError("Invalid request: bad request parameters: {0}".format(form.errors)) 466 467 if not flask.g.user.can_build_in(copr): 468 raise LegacyApiError("Invalid request: user {} is not allowed to build in the copr: {}" 469 .format(flask.g.user.username, copr.full_name)) 470 471 # create a new build 472 try: 473 # From URLs it can be created multiple builds at once 474 # so it can return a list 475 build = create_new_build() 476 db.session.commit() 477 ids = [build.id] if type(build) != list else [b.id for b in build] 478 infos.append("Build was added to {0}:".format(copr.name)) 479 for build_id in ids: 480 infos.append(" " + flask.url_for("coprs_ns.copr_build_redirect", 481 build_id=build_id, 482 _external=True)) 483 484 except (ActionInProgressException, InsufficientRightsException) as e: 485 raise LegacyApiError("Invalid request: {}".format(e)) 486 487 output = {"output": "ok", 488 "ids": ids, 489 "message": "\n".join(infos)} 490 491 return flask.jsonify(output)
492 493 494 @api_ns.route("/coprs/build_status/<int:build_id>/", methods=["GET"])
495 @api_login_required 496 -def build_status(build_id):
497 build = ComplexLogic.get_build_safe(build_id) 498 output = {"output": "ok", 499 "status": build.state} 500 return flask.jsonify(output)
501 502 503 @api_ns.route("/coprs/build_detail/<int:build_id>/", methods=["GET"]) 504 @api_ns.route("/coprs/build/<int:build_id>/", methods=["GET"])
505 -def build_detail(build_id):
506 build = ComplexLogic.get_build_safe(build_id) 507 508 chroots = {} 509 results_by_chroot = {} 510 for chroot in build.build_chroots: 511 chroots[chroot.name] = chroot.state 512 results_by_chroot[chroot.name] = chroot.result_dir_url 513 514 built_packages = None 515 if build.built_packages: 516 built_packages = build.built_packages.split("\n") 517 518 output = { 519 "output": "ok", 520 "status": build.state, 521 "project": build.copr.name, 522 "owner": build.copr.owner_name, 523 "results": build.results, 524 "built_pkgs": built_packages, 525 "src_version": build.pkg_version, 526 "chroots": chroots, 527 "submitted_on": build.submitted_on, 528 "started_on": build.min_started_on, 529 "ended_on": build.max_ended_on, 530 "src_pkg": build.pkgs, 531 "submitted_by": build.user.name, 532 "results_by_chroot": results_by_chroot 533 } 534 return flask.jsonify(output)
535 536 537 @api_ns.route("/coprs/cancel_build/<int:build_id>/", methods=["POST"])
538 @api_login_required 539 -def cancel_build(build_id):
540 build = ComplexLogic.get_build_safe(build_id) 541 542 try: 543 builds_logic.BuildsLogic.cancel_build(flask.g.user, build) 544 db.session.commit() 545 except exceptions.InsufficientRightsException as e: 546 raise LegacyApiError("Invalid request: {}".format(e)) 547 548 output = {'output': 'ok', 'status': "Build canceled"} 549 return flask.jsonify(output)
550 551 552 @api_ns.route("/coprs/delete_build/<int:build_id>/", methods=["POST"])
553 @api_login_required 554 -def delete_build(build_id):
555 build = ComplexLogic.get_build_safe(build_id) 556 557 try: 558 builds_logic.BuildsLogic.delete_build(flask.g.user, build) 559 db.session.commit() 560 except (exceptions.InsufficientRightsException,exceptions.ActionInProgressException) as e: 561 raise LegacyApiError("Invalid request: {}".format(e)) 562 563 output = {'output': 'ok', 'status': "Build deleted"} 564 return flask.jsonify(output)
565 566 567 @api_ns.route('/coprs/<username>/<coprname>/modify/', methods=["POST"])
568 @api_login_required 569 @api_req_with_copr 570 -def copr_modify(copr):
571 form = forms.CoprModifyForm(csrf_enabled=False) 572 573 if not form.validate_on_submit(): 574 raise LegacyApiError("Invalid request: {0}".format(form.errors)) 575 576 # .raw_data needs to be inspected to figure out whether the field 577 # was not sent or was sent empty 578 if form.description.raw_data and len(form.description.raw_data): 579 copr.description = form.description.data 580 if form.instructions.raw_data and len(form.instructions.raw_data): 581 copr.instructions = form.instructions.data 582 if form.repos.raw_data and len(form.repos.raw_data): 583 copr.repos = form.repos.data 584 if form.disable_createrepo.raw_data and len(form.disable_createrepo.raw_data): 585 copr.disable_createrepo = form.disable_createrepo.data 586 587 if "unlisted_on_hp" in flask.request.form: 588 copr.unlisted_on_hp = form.unlisted_on_hp.data 589 if "build_enable_net" in flask.request.form: 590 copr.build_enable_net = form.build_enable_net.data 591 if "auto_prune" in flask.request.form: 592 copr.auto_prune = form.auto_prune.data 593 594 try: 595 CoprsLogic.update(flask.g.user, copr) 596 if copr.group: # load group.id 597 _ = copr.group.id 598 db.session.commit() 599 except (exceptions.ActionInProgressException, 600 exceptions.InsufficientRightsException, 601 exceptions.NonAdminCannotDisableAutoPrunning) as e: 602 db.session.rollback() 603 raise LegacyApiError("Invalid request: {}".format(e)) 604 605 output = { 606 'output': 'ok', 607 'description': copr.description, 608 'instructions': copr.instructions, 609 'repos': copr.repos, 610 } 611 612 return flask.jsonify(output)
613 614 615 @api_ns.route('/coprs/<username>/<coprname>/modify/<chrootname>/', methods=["POST"])
616 @api_login_required 617 @api_req_with_copr 618 -def copr_modify_chroot(copr, chrootname):
619 """Deprecated to copr_edit_chroot""" 620 form = forms.ModifyChrootForm(csrf_enabled=False) 621 # chroot = coprs_logic.MockChrootsLogic.get_from_name(chrootname, active_only=True).first() 622 chroot = ComplexLogic.get_copr_chroot_safe(copr, chrootname) 623 624 if not form.validate_on_submit(): 625 raise LegacyApiError("Invalid request: {0}".format(form.errors)) 626 else: 627 coprs_logic.CoprChrootsLogic.update_chroot(flask.g.user, chroot, form.buildroot_pkgs.data) 628 db.session.commit() 629 630 output = {'output': 'ok', 'buildroot_pkgs': chroot.buildroot_pkgs} 631 return flask.jsonify(output)
632 633 634 @api_ns.route('/coprs/<username>/<coprname>/chroot/edit/<chrootname>/', methods=["POST"])
635 @api_login_required 636 @api_req_with_copr 637 -def copr_edit_chroot(copr, chrootname):
638 form = forms.ModifyChrootForm(csrf_enabled=False) 639 chroot = ComplexLogic.get_copr_chroot_safe(copr, chrootname) 640 641 if not form.validate_on_submit(): 642 raise LegacyApiError("Invalid request: {0}".format(form.errors)) 643 else: 644 buildroot_pkgs = repos = comps_xml = comps_name = None 645 if "buildroot_pkgs" in flask.request.form: 646 buildroot_pkgs = form.buildroot_pkgs.data 647 if "repos" in flask.request.form: 648 repos = form.repos.data 649 if form.upload_comps.has_file(): 650 comps_xml = form.upload_comps.data.stream.read() 651 comps_name = form.upload_comps.data.filename 652 if form.delete_comps.data: 653 coprs_logic.CoprChrootsLogic.remove_comps(flask.g.user, chroot) 654 coprs_logic.CoprChrootsLogic.update_chroot( 655 flask.g.user, chroot, buildroot_pkgs, repos, comps=comps_xml, comps_name=comps_name) 656 db.session.commit() 657 658 output = { 659 "output": "ok", 660 "message": "Edit chroot operation was successful.", 661 "chroot": chroot.to_dict(), 662 } 663 return flask.jsonify(output)
664 665 666 @api_ns.route('/coprs/<username>/<coprname>/detail/<chrootname>/', methods=["GET"])
667 @api_req_with_copr 668 -def copr_chroot_details(copr, chrootname):
669 """Deprecated to copr_get_chroot""" 670 chroot = ComplexLogic.get_copr_chroot_safe(copr, chrootname) 671 output = {'output': 'ok', 'buildroot_pkgs': chroot.buildroot_pkgs} 672 return flask.jsonify(output)
673 674 @api_ns.route('/coprs/<username>/<coprname>/chroot/get/<chrootname>/', methods=["GET"])
675 @api_req_with_copr 676 -def copr_get_chroot(copr, chrootname):
677 chroot = ComplexLogic.get_copr_chroot_safe(copr, chrootname) 678 output = {'output': 'ok', 'chroot': chroot.to_dict()} 679 return flask.jsonify(output)
680
681 @api_ns.route("/coprs/search/") 682 @api_ns.route("/coprs/search/<project>/") 683 -def api_coprs_search_by_project(project=None):
684 """ Return the list of coprs found in search by the given text. 685 project is taken either from GET params or from the URL itself 686 (in this order). 687 688 :arg project: the text one would like find for coprs. 689 690 """ 691 project = flask.request.args.get("project", None) or project 692 if not project: 693 raise LegacyApiError("No project found.") 694 695 try: 696 query = CoprsLogic.get_multiple_fulltext(project) 697 698 repos = query.all() 699 output = {"output": "ok", "repos": []} 700 for repo in repos: 701 output["repos"].append({"username": repo.user.name, 702 "coprname": repo.name, 703 "description": repo.description}) 704 except ValueError as e: 705 raise LegacyApiError("Server error: {}".format(e)) 706 707 return flask.jsonify(output)
708
709 710 @api_ns.route("/playground/list/") 711 -def playground_list():
712 """ Return list of coprs which are part of playground """ 713 query = CoprsLogic.get_playground() 714 repos = query.all() 715 output = {"output": "ok", "repos": []} 716 for repo in repos: 717 output["repos"].append({"username": repo.owner_name, 718 "coprname": repo.name, 719 "chroots": [chroot.name for chroot in repo.active_chroots]}) 720 721 jsonout = flask.jsonify(output) 722 jsonout.status_code = 200 723 return jsonout
724 725 726 @api_ns.route("/coprs/<username>/<coprname>/monitor/", methods=["GET"])
727 @api_req_with_copr 728 -def monitor(copr):
729 monitor_data = builds_logic.BuildsMonitorLogic.get_monitor_data(copr) 730 output = MonitorWrapper(copr, monitor_data).to_dict() 731 return flask.jsonify(output)
732 733 ############################################################################### 734 735 @api_ns.route("/coprs/<username>/<coprname>/package/add/<source_type_text>/", methods=["POST"])
736 @api_login_required 737 @api_req_with_copr 738 -def copr_add_package(copr, source_type_text):
739 return process_package_add_or_edit(copr, source_type_text)
740 741 742 @api_ns.route("/coprs/<username>/<coprname>/package/<package_name>/edit/<source_type_text>/", methods=["POST"])
743 @api_login_required 744 @api_req_with_copr 745 -def copr_edit_package(copr, package_name, source_type_text):
746 try: 747 package = PackagesLogic.get(copr.id, package_name)[0] 748 except IndexError: 749 raise LegacyApiError("Package {name} does not exists in copr {copr}.".format(name=package_name, copr=copr.full_name)) 750 return process_package_add_or_edit(copr, source_type_text, package=package)
751
752 753 -def process_package_add_or_edit(copr, source_type_text, package=None):
754 try: 755 form = forms.get_package_form_cls_by_source_type_text(source_type_text)(csrf_enabled=False) 756 except UnknownSourceTypeException: 757 raise LegacyApiError("Unsupported package source type {source_type_text}".format(source_type_text=source_type_text)) 758 759 if form.validate_on_submit(): 760 if not package: 761 try: 762 package = PackagesLogic.add(flask.app.g.user, copr, form.package_name.data) 763 except InsufficientRightsException: 764 raise LegacyApiError("Insufficient permissions.") 765 except DuplicateException: 766 raise LegacyApiError("Package {0} already exists in copr {1}.".format(form.package_name.data, copr.full_name)) 767 768 package.source_type = helpers.BuildSourceEnum(source_type_text) 769 package.webhook_rebuild = form.webhook_rebuild.data 770 package.source_json = form.source_json 771 772 db.session.add(package) 773 db.session.commit() 774 else: 775 raise LegacyApiError(form.errors) 776 777 return flask.jsonify({ 778 "output": "ok", 779 "message": "Create or edit operation was successful.", 780 "package": package.to_dict(), 781 })
782
783 784 -def get_package_record_params():
785 params = {} 786 if flask.request.args.get('with_latest_build'): 787 params['with_latest_build'] = True 788 if flask.request.args.get('with_latest_succeeded_build'): 789 params['with_latest_succeeded_build'] = True 790 if flask.request.args.get('with_all_builds'): 791 params['with_all_builds'] = True 792 return params
793
794 795 -def generate_package_list(query, params):
796 """ 797 A lagging generator to stream JSON so we don't have to hold everything in memory 798 This is a little tricky, as we need to omit the last comma to make valid JSON, 799 thus we use a lagging generator, similar to http://stackoverflow.com/questions/1630320/ 800 """ 801 packages = query.__iter__() 802 try: 803 prev_package = next(packages) # get first result 804 except StopIteration: 805 # StopIteration here means the length was zero, so yield a valid packages doc and stop 806 yield '{"packages": []}' 807 raise StopIteration 808 # We have some packages. First, yield the opening json 809 yield '{"packages": [' 810 # Iterate over the packages 811 for package in packages: 812 yield json.dumps(prev_package.to_dict(**params)) + ', ' 813 prev_package = package 814 # Now yield the last iteration without comma but with the closing brackets 815 yield json.dumps(prev_package.to_dict(**params)) + ']}'
816 817 818 @api_ns.route("/coprs/<username>/<coprname>/package/list/", methods=["GET"])
819 @api_req_with_copr 820 -def copr_list_packages(copr):
821 packages = PackagesLogic.get_all(copr.id) 822 params = get_package_record_params() 823 return flask.Response(generate_package_list(packages, params), content_type='application/json')
824 #return flask.jsonify({"packages": [package.to_dict(**params) for package in packages]}) 825 826 827 @api_ns.route("/coprs/<username>/<coprname>/package/get/<package_name>/", methods=["GET"])
828 @api_req_with_copr 829 -def copr_get_package(copr, package_name):
830 try: 831 package = PackagesLogic.get(copr.id, package_name)[0] 832 except IndexError: 833 raise LegacyApiError("No package with name {name} in copr {copr}".format(name=package_name, copr=copr.name)) 834 835 params = get_package_record_params() 836 return flask.jsonify({'package': package.to_dict(**params)})
837 838 839 @api_ns.route("/coprs/<username>/<coprname>/package/delete/<package_name>/", methods=["POST"])
840 @api_login_required 841 @api_req_with_copr 842 -def copr_delete_package(copr, package_name):
843 try: 844 package = PackagesLogic.get(copr.id, package_name)[0] 845 except IndexError: 846 raise LegacyApiError("No package with name {name} in copr {copr}".format(name=package_name, copr=copr.name)) 847 848 try: 849 PackagesLogic.delete_package(flask.g.user, package) 850 db.session.commit() 851 except (InsufficientRightsException, ActionInProgressException) as e: 852 raise LegacyApiError(str(e)) 853 854 return flask.jsonify({ 855 "output": "ok", 856 "message": "Package was successfully deleted.", 857 'package': package.to_dict(), 858 })
859 860 861 @api_ns.route("/coprs/<username>/<coprname>/package/reset/<package_name>/", methods=["POST"])
862 @api_login_required 863 @api_req_with_copr 864 -def copr_reset_package(copr, package_name):
865 try: 866 package = PackagesLogic.get(copr.id, package_name)[0] 867 except IndexError: 868 raise LegacyApiError("No package with name {name} in copr {copr}".format(name=package_name, copr=copr.name)) 869 870 try: 871 PackagesLogic.reset_package(flask.g.user, package) 872 db.session.commit() 873 except InsufficientRightsException as e: 874 raise LegacyApiError(str(e)) 875 876 return flask.jsonify({ 877 "output": "ok", 878 "message": "Package's default source was successfully reseted.", 879 'package': package.to_dict(), 880 })
881 882 883 @api_ns.route("/coprs/<username>/<coprname>/package/build/<package_name>/", methods=["POST"])
884 @api_login_required 885 @api_req_with_copr 886 -def copr_build_package(copr, package_name):
887 form = forms.BuildFormRebuildFactory.create_form_cls(copr.active_chroots)(csrf_enabled=False) 888 889 try: 890 package = PackagesLogic.get(copr.id, package_name)[0] 891 except IndexError: 892 raise LegacyApiError("No package with name {name} in copr {copr}".format(name=package_name, copr=copr.name)) 893 894 if form.validate_on_submit(): 895 try: 896 build = PackagesLogic.build_package(flask.g.user, copr, package, form.selected_chroots, **form.data) 897 db.session.commit() 898 except (InsufficientRightsException, ActionInProgressException, NoPackageSourceException) as e: 899 raise LegacyApiError(str(e)) 900 else: 901 raise LegacyApiError(form.errors) 902 903 return flask.jsonify({ 904 "output": "ok", 905 "ids": [build.id], 906 "message": "Build was added to {0}.".format(copr.name) 907 })
908 909 910 @api_ns.route("/coprs/<username>/<coprname>/module/build/", methods=["POST"])
911 @api_login_required 912 @api_req_with_copr 913 -def copr_build_module(copr):
914 form = forms.ModuleFormUploadFactory(csrf_enabled=False) 915 if not form.validate_on_submit(): 916 # @TODO Prettier error 917 raise LegacyApiError(form.errors) 918 919 modulemd = form.modulemd.data.read() 920 module = ModulesLogic.from_modulemd(modulemd) 921 try: 922 module = ModulesLogic.add(flask.g.user, copr, module) 923 db.session.flush() 924 ActionsLogic.send_build_module(flask.g.user, copr, module) 925 db.session.commit() 926 927 return flask.jsonify({ 928 "output": "ok", 929 "message": "Module build was submitted", 930 "modulemd": modulemd, 931 }) 932 933 except sqlalchemy.exc.IntegrityError: 934 raise LegacyApiError({"nsv": ["Module {} already exists".format(module.nsv)]})
935 936 937 @api_ns.route("/coprs/<username>/<coprname>/build-config/<chroot>/", methods=["GET"]) 938 @api_ns.route("/g/<group_name>/<coprname>/build-config/<chroot>/", methods=["GET"])
939 @api_req_with_copr 940 -def copr_build_config(copr, chroot):
941 """ 942 Generate build configuration. 943 """ 944 output = { 945 "output": "ok", 946 "build_config": generate_build_config(copr, chroot), 947 } 948 949 if not output['build_config']: 950 raise LegacyApiError('Chroot not found.') 951 952 return flask.jsonify(output)
953 954 955 @api_ns.route("/module/repo/", methods=["POST"])
956 -def copr_module_repo():
957 """ 958 :return: URL to a DNF repository for the module 959 """ 960 form = forms.ModuleRepo(csrf_enabled=False) 961 if not form.validate_on_submit(): 962 raise LegacyApiError(form.errors) 963 964 copr = ComplexLogic.get_copr_by_owner_safe(form.owner.data, form.copr.data) 965 module = ModulesLogic.get_by_nsv(copr, form.name.data, form.stream.data, form.version.data).first() 966 967 return flask.jsonify({"output": "ok", "repo": module.repo_url(form.arch.data)})
968