Package coprs :: Package views :: Package apiv3_ns :: Module apiv3_builds
[hide private]
[frames] | no frames]

Source Code for Module coprs.views.apiv3_ns.apiv3_builds

  1  import os 
  2  import flask 
  3  from werkzeug.datastructures import MultiDict 
  4  from . import get_copr, file_upload, query_params, pagination, Paginator, json2form, GET, POST, PUT, DELETE 
  5  from .json2form import get_form_compatible_data, without_empty_fields 
  6  from werkzeug import secure_filename 
  7  from coprs import db, forms, models 
  8  from coprs.helpers import StatusEnum 
  9  from coprs.exceptions import (ApiError, InsufficientRightsException, ActionInProgressException, 
 10                                BadRequest, AccessRestricted) 
 11  from coprs.views.misc import api_login_required 
 12  from coprs.views.apiv3_ns import apiv3_ns 
 13  from coprs.logic.complex_logic import ComplexLogic 
 14  from coprs.logic.builds_logic import BuildsLogic 
15 16 17 -def to_dict(build):
18 return { 19 "id": build.id, 20 "state": build.state, 21 "projectname": build.copr.name, 22 "ownername": build.copr.owner_name, 23 "repo_url": build.copr.repo_url, 24 "source_package": {"name": build.package_name, "version": build.pkg_version, "url": build.srpm_url}, 25 "submitted_on": build.submitted_on, 26 "started_on": build.min_started_on, 27 "ended_on": build.max_ended_on, 28 "submitter": build.user.name if build.user else None, 29 "chroots": [chroot.name for chroot in build.build_chroots], 30 }
31
32 33 -def to_source_chroot(build):
34 return { 35 "state": StatusEnum(build.source_status), 36 "result_url": os.path.dirname(build.import_log_url_backend), 37 # @TODO Do we have such information stored? 38 # "started_on": None, 39 # "ended_on": None 40 }
41
42 43 -def to_source_build_config(build):
44 return { 45 "source_type": build.source_type_text, 46 "source_dict": build.source_json_dict, 47 "memory_limit": build.memory_reqs, 48 "timeout": build.timeout, 49 "is_background": build.is_background, 50 }
51
52 53 -def rename_fields(input):
54 replace = { 55 "source_build_method": "srpm_build_method", 56 } 57 output = input.copy() 58 for from_name, to_name in replace.items(): 59 if from_name not in output: 60 continue 61 output[to_name] = output.pop(from_name) 62 return output
63
64 65 -def render_build(build):
66 return flask.jsonify(to_dict(build))
67
68 69 @apiv3_ns.route("/build/<int:build_id>/", methods=GET) 70 -def get_build(build_id):
71 build = ComplexLogic.get_build_safe(build_id) 72 return render_build(build)
73
74 75 @apiv3_ns.route("/build/list/", methods=GET) 76 @pagination() 77 @query_params() 78 -def get_build_list(ownername, projectname, packagename=None, status=None, **kwargs):
79 copr = get_copr(ownername, projectname) 80 query = BuildsLogic.get_multiple_by_copr(copr) 81 if packagename: 82 query = BuildsLogic.filter_by_package_name(query, packagename) 83 84 # WORKAROUND 85 # We can't filter builds by status directly in the database, because we 86 # use a logic in Build.status property to determine a build status. 87 # Therefore if we want to filter by `status`, we need to query all builds 88 # and filter them in the application and then return the desired number. 89 limit = kwargs["limit"] 90 paginator_limit = None if status else kwargs["limit"] 91 del kwargs["limit"] 92 93 paginator = Paginator(query, models.Build, limit=paginator_limit, **kwargs) 94 builds = paginator.map(to_dict) 95 96 if status: 97 builds = [b for b in builds if b["state"] == status][:limit] 98 paginator.limit = limit 99 100 return flask.jsonify(items=builds, meta=paginator.meta)
101
102 103 @apiv3_ns.route("/build/source-chroot/<int:build_id>/", methods=GET) 104 -def get_source_chroot(build_id):
105 build = ComplexLogic.get_build_safe(build_id) 106 return flask.jsonify(to_source_chroot(build))
107
108 109 @apiv3_ns.route("/build/source-build-config/<int:build_id>/", methods=GET) 110 -def get_source_build_config(build_id):
111 build = ComplexLogic.get_build_safe(build_id) 112 return flask.jsonify(to_source_build_config(build))
113
114 115 @apiv3_ns.route("/build/cancel/<int:build_id>", methods=PUT) 116 @api_login_required 117 -def cancel_build(build_id):
118 build = ComplexLogic.get_build_safe(build_id) 119 BuildsLogic.cancel_build(flask.g.user, build) 120 db.session.commit() 121 return render_build(build)
122
123 124 @apiv3_ns.route("/build/create/url", methods=POST) 125 @api_login_required 126 -def create_from_url():
127 copr = get_copr() 128 data = get_form_compatible_data() 129 form = forms.BuildFormUrlFactory(copr.active_chroots)(data, csrf_enabled=False) 130 131 def create_new_build(): 132 # create separate build for each package 133 pkgs = form.pkgs.data.split("\n") 134 return [BuildsLogic.create_new_from_url( 135 flask.g.user, copr, 136 url=pkg, 137 chroot_names=form.selected_chroots, 138 background=form.background.data, 139 ) for pkg in pkgs]
140 return process_creating_new_build(copr, form, create_new_build) 141
142 143 @apiv3_ns.route("/build/create/upload", methods=POST) 144 @api_login_required 145 @file_upload() 146 -def create_from_upload():
147 copr = get_copr() 148 data = get_form_compatible_data() 149 form = forms.BuildFormUploadFactory(copr.active_chroots)(data, csrf_enabled=False) 150 151 def create_new_build(): 152 return BuildsLogic.create_new_from_upload( 153 flask.g.user, copr, 154 f_uploader=lambda path: form.pkgs.data.save(path), 155 orig_filename=secure_filename(form.pkgs.data.filename), 156 chroot_names=form.selected_chroots, 157 background=form.background.data, 158 )
159 return process_creating_new_build(copr, form, create_new_build) 160
161 162 @apiv3_ns.route("/build/create/scm", methods=POST) 163 @api_login_required 164 -def create_from_scm():
165 copr = get_copr() 166 data = rename_fields(get_form_compatible_data()) 167 form = forms.BuildFormScmFactory(copr.active_chroots)(data, csrf_enabled=False) 168 169 def create_new_build(): 170 return BuildsLogic.create_new_from_scm( 171 flask.g.user, 172 copr, 173 scm_type=form.scm_type.data, 174 clone_url=form.clone_url.data, 175 committish=form.committish.data, 176 subdirectory=form.subdirectory.data, 177 spec=form.spec.data, 178 srpm_build_method=form.srpm_build_method.data, 179 chroot_names=form.selected_chroots, 180 background=form.background.data, 181 )
182 return process_creating_new_build(copr, form, create_new_build) 183
184 185 @apiv3_ns.route("/build/create/pypi", methods=POST) 186 @api_login_required 187 -def create_from_pypi():
188 copr = get_copr() 189 data = MultiDict(json2form.without_empty_fields(json2form.get_input())) 190 form = forms.BuildFormPyPIFactory(copr.active_chroots)(data, csrf_enabled=False) 191 192 # TODO: automatically prepopulate all form fields with their defaults 193 if not form.python_versions.data: 194 form.python_versions.data = form.python_versions.default 195 196 def create_new_build(): 197 return BuildsLogic.create_new_from_pypi( 198 flask.g.user, 199 copr, 200 form.pypi_package_name.data, 201 form.pypi_package_version.data, 202 form.python_versions.data, 203 form.selected_chroots, 204 background=form.background.data, 205 )
206 return process_creating_new_build(copr, form, create_new_build) 207
208 209 @apiv3_ns.route("/build/create/rubygems", methods=POST) 210 @api_login_required 211 -def create_from_rubygems():
212 copr = get_copr() 213 data = get_form_compatible_data() 214 form = forms.BuildFormRubyGemsFactory(copr.active_chroots)(data, csrf_enabled=False) 215 216 def create_new_build(): 217 return BuildsLogic.create_new_from_rubygems( 218 flask.g.user, 219 copr, 220 form.gem_name.data, 221 form.selected_chroots, 222 background=form.background.data, 223 )
224 return process_creating_new_build(copr, form, create_new_build) 225
226 227 @apiv3_ns.route("/build/create/custom", methods=POST) 228 @api_login_required 229 -def create_from_custom():
230 copr = get_copr() 231 data = get_form_compatible_data() 232 form = forms.BuildFormCustomFactory(copr.active_chroots)(data, csrf_enabled=False) 233 234 def create_new_build(): 235 return BuildsLogic.create_new_from_custom( 236 flask.g.user, 237 copr, 238 form.script.data, 239 form.chroot.data, 240 form.builddeps.data, 241 form.resultdir.data, 242 chroot_names=form.selected_chroots, 243 background=form.background.data, 244 )
245 return process_creating_new_build(copr, form, create_new_build) 246
247 248 -def process_creating_new_build(copr, form, create_new_build):
249 if not form.validate_on_submit(): 250 raise BadRequest("Bad request parameters: {0}".format(form.errors)) 251 252 if not flask.g.user.can_build_in(copr): 253 raise AccessRestricted("User {} is not allowed to build in the copr: {}" 254 .format(flask.g.user.username, copr.full_name)) 255 256 # From URLs it can be created multiple builds at once 257 # so it can return a list 258 build = create_new_build() 259 db.session.commit() 260 261 if type(build) == list: 262 builds = [build] if type(build) != list else build 263 return flask.jsonify(items=[to_dict(b) for b in builds], meta={}) 264 return flask.jsonify(to_dict(build))
265
266 267 @apiv3_ns.route("/build/delete/<int:build_id>", methods=DELETE) 268 @api_login_required 269 -def delete_build(build_id):
270 build = ComplexLogic.get_build_safe(build_id) 271 build_dict = to_dict(build) 272 BuildsLogic.delete_build(flask.g.user, build) 273 db.session.commit() 274 return flask.jsonify(build_dict)
275