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 
  6  from werkzeug import secure_filename 
  7  from copr_common.enums import StatusEnum 
  8  from coprs import db, forms, models 
  9  from coprs.exceptions import (BadRequest, AccessRestricted) 
 10  from coprs.views.misc import api_login_required 
 11  from coprs.views.apiv3_ns import apiv3_ns 
 12  from coprs.logic.complex_logic import ComplexLogic 
 13  from coprs.logic.builds_logic import BuildsLogic 
14 15 16 -def to_dict(build):
17 return { 18 "id": build.id, 19 "state": build.state, 20 "projectname": build.copr.name, 21 "ownername": build.copr.owner_name, 22 "repo_url": build.copr.repo_url, 23 "source_package": {"name": build.package_name, "version": build.pkg_version, "url": build.srpm_url}, 24 "submitted_on": build.submitted_on, 25 "started_on": build.min_started_on, 26 "ended_on": build.max_ended_on, 27 "submitter": build.user.name if build.user else None, 28 "chroots": [chroot.name for chroot in build.build_chroots], 29 "project_dirname": build.copr_dir.name, 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, meta={'csrf': 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 copr_dirname=form.project_dirname.data, 140 ) for pkg in pkgs]
141 return process_creating_new_build(copr, form, create_new_build) 142
143 144 @apiv3_ns.route("/build/create/upload", methods=POST) 145 @api_login_required 146 @file_upload() 147 -def create_from_upload():
148 copr = get_copr() 149 data = get_form_compatible_data() 150 form = forms.BuildFormUploadFactory(copr.active_chroots)(data, meta={'csrf': False}) 151 152 def create_new_build(): 153 return BuildsLogic.create_new_from_upload( 154 flask.g.user, copr, 155 f_uploader=lambda path: form.pkgs.data.save(path), 156 orig_filename=secure_filename(form.pkgs.data.filename), 157 chroot_names=form.selected_chroots, 158 background=form.background.data, 159 copr_dirname=form.project_dirname.data, 160 )
161 return process_creating_new_build(copr, form, create_new_build) 162
163 164 @apiv3_ns.route("/build/create/scm", methods=POST) 165 @api_login_required 166 -def create_from_scm():
167 copr = get_copr() 168 data = rename_fields(get_form_compatible_data()) 169 form = forms.BuildFormScmFactory(copr.active_chroots)(data, meta={'csrf': False}) 170 171 def create_new_build(): 172 return BuildsLogic.create_new_from_scm( 173 flask.g.user, 174 copr, 175 scm_type=form.scm_type.data, 176 clone_url=form.clone_url.data, 177 committish=form.committish.data, 178 subdirectory=form.subdirectory.data, 179 spec=form.spec.data, 180 srpm_build_method=form.srpm_build_method.data, 181 chroot_names=form.selected_chroots, 182 background=form.background.data, 183 copr_dirname=form.project_dirname.data, 184 )
185 return process_creating_new_build(copr, form, create_new_build) 186
187 188 @apiv3_ns.route("/build/create/pypi", methods=POST) 189 @api_login_required 190 -def create_from_pypi():
191 copr = get_copr() 192 data = MultiDict(json2form.without_empty_fields(json2form.get_input())) 193 form = forms.BuildFormPyPIFactory(copr.active_chroots)(data, meta={'csrf': False}) 194 195 # TODO: automatically prepopulate all form fields with their defaults 196 if not form.python_versions.data: 197 form.python_versions.data = form.python_versions.default 198 199 def create_new_build(): 200 return BuildsLogic.create_new_from_pypi( 201 flask.g.user, 202 copr, 203 form.pypi_package_name.data, 204 form.pypi_package_version.data, 205 form.spec_template.data, 206 form.python_versions.data, 207 form.selected_chroots, 208 background=form.background.data, 209 copr_dirname=form.project_dirname.data, 210 )
211 return process_creating_new_build(copr, form, create_new_build) 212
213 214 @apiv3_ns.route("/build/create/rubygems", methods=POST) 215 @api_login_required 216 -def create_from_rubygems():
217 copr = get_copr() 218 data = get_form_compatible_data() 219 form = forms.BuildFormRubyGemsFactory(copr.active_chroots)(data, meta={'csrf': False}) 220 221 def create_new_build(): 222 return BuildsLogic.create_new_from_rubygems( 223 flask.g.user, 224 copr, 225 form.gem_name.data, 226 form.selected_chroots, 227 background=form.background.data, 228 copr_dirname=form.project_dirname.data, 229 )
230 return process_creating_new_build(copr, form, create_new_build) 231
232 233 @apiv3_ns.route("/build/create/custom", methods=POST) 234 @api_login_required 235 -def create_from_custom():
236 copr = get_copr() 237 data = get_form_compatible_data() 238 form = forms.BuildFormCustomFactory(copr.active_chroots)(data, meta={'csrf': False}) 239 240 def create_new_build(): 241 return BuildsLogic.create_new_from_custom( 242 flask.g.user, 243 copr, 244 form.script.data, 245 form.chroot.data, 246 form.builddeps.data, 247 form.resultdir.data, 248 chroot_names=form.selected_chroots, 249 background=form.background.data, 250 copr_dirname=form.project_dirname.data, 251 )
252 return process_creating_new_build(copr, form, create_new_build) 253
254 255 -def process_creating_new_build(copr, form, create_new_build):
256 if not form.validate_on_submit(): 257 raise BadRequest("Bad request parameters: {0}".format(form.errors)) 258 259 if not flask.g.user.can_build_in(copr): 260 raise AccessRestricted("User {} is not allowed to build in the copr: {}" 261 .format(flask.g.user.username, copr.full_name)) 262 263 # From URLs it can be created multiple builds at once 264 # so it can return a list 265 build = create_new_build() 266 db.session.commit() 267 268 if type(build) == list: 269 builds = [build] if type(build) != list else build 270 return flask.jsonify(items=[to_dict(b) for b in builds], meta={}) 271 return flask.jsonify(to_dict(build))
272
273 274 @apiv3_ns.route("/build/delete/<int:build_id>", methods=DELETE) 275 @api_login_required 276 -def delete_build(build_id):
277 build = ComplexLogic.get_build_safe(build_id) 278 build_dict = to_dict(build) 279 BuildsLogic.delete_build(flask.g.user, build) 280 db.session.commit() 281 return flask.jsonify(build_dict)
282