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