def create(self, request, extra_fields={}, local_site=None, *args, **kwargs): """Creates a new diff by parsing an uploaded diff file. This will implicitly create the new Review Request draft, which can be updated separately and then published. This accepts a unified diff file, validates it, and stores it along with the draft of a review request. The new diff will have a revision of 0. A parent diff can be uploaded along with the main diff. A parent diff is a diff based on an existing commit in the repository, which will be applied before the main diff. The parent diff will not be included in the diff viewer. It's useful when developing a change based on a branch that is not yet committed. In this case, a parent diff of the parent branch would be provided along with the diff of the new commit, and only the new commit will be shown. It is expected that the client will send the data as part of a :mimetype:`multipart/form-data` mimetype. The main diff's name and content would be stored in the ``path`` field. If a parent diff is provided, its name and content would be stored in the ``parent_diff_path`` field. An example of this would be:: -- SoMe BoUnDaRy Content-Disposition: form-data; name=path; filename="foo.diff" <Unified Diff Content Here> -- SoMe BoUnDaRy -- Extra data can be stored later lookup. See :ref:`webapi2.0-extra-data` for more information. """ # Prevent a circular dependency, as ReviewRequestDraftResource # needs DraftDiffResource, which needs DiffResource. from reviewboard.webapi.resources.review_request_draft import \ ReviewRequestDraftResource try: review_request = \ resources.review_request.get_object(request, *args, **kwargs) except ReviewRequest.DoesNotExist: return DOES_NOT_EXIST if not review_request.is_mutable_by(request.user): return self.get_no_access_error(request) if review_request.repository is None: return INVALID_ATTRIBUTE, { 'reason': 'This review request was created as attachments-' 'only, with no repository.', } elif review_request.created_with_history: assert dvcs_feature.is_enabled(request=request) if 'path' in request.FILES: return INVALID_FORM_DATA, { 'reason': ( 'This review request was created with support for ' 'multiple commits.\n' '\n' 'Create an empty diff revision and upload commits to ' 'that instead.' ), } diffset = DiffSet.objects.create_empty( repository=review_request.repository, base_commit_id=request.POST.get('base_commit_id')) diffset.update_revision_from_history( review_request.diffset_history) diffset.save(update_fields=('revision',)) else: form_data = request.POST.copy() form = UploadDiffForm(review_request, data=form_data, files=request.FILES, request=request) if not form.is_valid(): return INVALID_FORM_DATA, { 'fields': self._get_form_errors(form), } try: diffset = form.create() except FileNotFoundError as e: return REPO_FILE_NOT_FOUND, { 'file': e.path, 'revision': six.text_type(e.revision) } except EmptyDiffError as e: return DIFF_EMPTY except DiffTooBigError as e: return DIFF_TOO_BIG, { 'reason': six.text_type(e), 'max_size': e.max_diff_size, } except Exception as e: # This could be very wrong, but at least they'll see the error. # We probably want a new error type for this. logging.error("Error uploading new diff: %s", e, exc_info=1, request=request) return INVALID_FORM_DATA, { 'fields': { 'path': [six.text_type(e)] } } discarded_diffset = None try: draft = review_request.draft.get() if draft.diffset and draft.diffset != diffset: discarded_diffset = draft.diffset except ReviewRequestDraft.DoesNotExist: try: draft = ReviewRequestDraftResource.prepare_draft( request, review_request) except PermissionDenied: return self.get_no_access_error(request) draft.diffset = diffset # We only want to add default reviewers the first time. Was bug 318. if review_request.can_add_default_reviewers(): draft.add_default_reviewers() draft.save() if extra_fields: try: self.import_extra_data(diffset, diffset.extra_data, extra_fields) except ImportExtraDataError as e: return e.error_payload diffset.save(update_fields=['extra_data']) if discarded_diffset: discarded_diffset.delete() # E-mail gets sent when the draft is saved. return 201, { self.item_result_key: diffset, }
def update(self, request, status=None, changenum=None, commit_id=None, description=None, *args, **kwargs): """Updates the status of the review request. The only supported update to a review request's resource is to change the status, the associated server-side, change number, or to update information from the existing change number. The status can be set in order to close the review request as discarded or submitted, or to reopen as pending. The change number can either be changed to a new number, or the current change number can be passed. In either case, a new draft will be created or an existing one updated to include information from the server based on the change number. Changes to a review request's fields, such as the summary or the list of reviewers, is made on the Review Request Draft resource. This can be accessed through the ``draft`` link. Only when that draft is published will the changes end up back in this resource. """ try: review_request = \ resources.review_request.get_object(request, *args, **kwargs) except ObjectDoesNotExist: return DOES_NOT_EXIST if not self.has_modify_permissions(request, review_request): return self._no_access_error(request.user) if (status is not None and (review_request.status != string_to_status(status) or review_request.status != ReviewRequest.PENDING_REVIEW)): try: if status in self._close_type_map: review_request.close(self._close_type_map[status], request.user, description) elif status == 'pending': review_request.reopen(request.user) else: raise AssertionError("Code path for invalid status '%s' " "should never be reached." % status) except PermissionError: return self._no_access_error(request.user) if changenum is not None and commit_id is None: commit_id = str(changenum) if commit_id is not None: if commit_id != review_request.commit: review_request.update_commit_id(commit_id, request.user) try: draft = ReviewRequestDraftResource.prepare_draft( request, review_request) except PermissionDenied: return PERMISSION_DENIED try: draft.update_from_commit_id(commit_id) except InvalidChangeNumberError: return INVALID_CHANGE_NUMBER draft.save() review_request.reopen() return 200, { self.item_result_key: review_request, }
def update(self, request, status=None, changenum=None, description=None, extra_fields={}, *args, **kwargs): """Updates the status of the review request. The only supported update to a review request's resource is to change the status, the associated server-side, change number, or to update information from the existing change number. The status can be set in order to close the review request as discarded or submitted, or to reopen as pending. For Perforce, a change number can either be changed to a new number, or the current change number can be passed. In either case, a new draft will be created or an existing one updated to include information from the server based on the change number. This behavior is deprecated, and instead, the commit_id field should be set on the draft. Changes to a review request's fields, such as the summary or the list of reviewers, is made on the Review Request Draft resource. This can be accessed through the ``draft`` link. Only when that draft is published will the changes end up back in this resource. Extra data can be stored on the review request for later lookup by passing ``extra_data.key_name=value``. The ``key_name`` and ``value`` can be any valid strings. Passing a blank ``value`` will remove the key. The ``extra_data.`` prefix is required. """ try: review_request = \ resources.review_request.get_object(request, *args, **kwargs) except ObjectDoesNotExist: return DOES_NOT_EXIST is_mutating_field = ( changenum is not None or extra_fields ) if ((is_mutating_field and not self.has_modify_permissions(request, review_request)) or (status is not None and not review_request.is_status_mutable_by(request.user))): return self._no_access_error(request.user) if (status is not None and (review_request.status != string_to_status(status) or review_request.status != ReviewRequest.PENDING_REVIEW)): try: if status in self._close_type_map: review_request.close(self._close_type_map[status], request.user, description) elif status == 'pending': review_request.reopen(request.user) else: raise AssertionError("Code path for invalid status '%s' " "should never be reached." % status) except PermissionError: return self._no_access_error(request.user) # Preserve the old changenum behavior. if changenum is not None: if changenum != review_request.changenum: review_request.commit = changenum try: draft = ReviewRequestDraftResource.prepare_draft( request, review_request) except PermissionDenied: return PERMISSION_DENIED try: draft.update_from_commit_id(six.text_type(changenum)) except InvalidChangeNumberError: return INVALID_CHANGE_NUMBER except EmptyChangeSetError: return EMPTY_CHANGESET draft.save() review_request.reopen() if extra_fields: self._import_extra_data(review_request.extra_data, extra_fields) review_request.save(update_fields=['extra_data']) return 200, { self.item_result_key: review_request, }
def create(self, request, extra_fields={}, local_site=None, *args, **kwargs): """Creates a new diff by parsing an uploaded diff file. This will implicitly create the new Review Request draft, which can be updated separately and then published. This accepts a unified diff file, validates it, and stores it along with the draft of a review request. The new diff will have a revision of 0. A parent diff can be uploaded along with the main diff. A parent diff is a diff based on an existing commit in the repository, which will be applied before the main diff. The parent diff will not be included in the diff viewer. It's useful when developing a change based on a branch that is not yet committed. In this case, a parent diff of the parent branch would be provided along with the diff of the new commit, and only the new commit will be shown. It is expected that the client will send the data as part of a :mimetype:`multipart/form-data` mimetype. The main diff's name and content would be stored in the ``path`` field. If a parent diff is provided, its name and content would be stored in the ``parent_diff_path`` field. An example of this would be:: -- SoMe BoUnDaRy Content-Disposition: form-data; name=path; filename="foo.diff" <Unified Diff Content Here> -- SoMe BoUnDaRy -- Extra data can be stored later lookup. See :ref:`webapi2.0-extra-data` for more information. """ # Prevent a circular dependency, as ReviewRequestDraftResource # needs DraftDiffResource, which needs DiffResource. from reviewboard.webapi.resources.review_request_draft import \ ReviewRequestDraftResource try: review_request = \ resources.review_request.get_object(request, *args, **kwargs) except ReviewRequest.DoesNotExist: return DOES_NOT_EXIST if not review_request.is_mutable_by(request.user): return self.get_no_access_error(request) if review_request.repository is None: return INVALID_ATTRIBUTE, { 'reason': 'This review request was created as attachments-' 'only, with no repository.', } elif review_request.created_with_history: assert dvcs_feature.is_enabled(request=request) if 'path' in request.FILES: return INVALID_FORM_DATA, { 'reason': ('This review request was created with support for ' 'multiple commits.\n' '\n' 'Create an empty diff revision and upload commits to ' 'that instead.'), } diffset = DiffSet.objects.create_empty( repository=review_request.repository, base_commit_id=request.POST.get('base_commit_id')) diffset.update_revision_from_history( review_request.diffset_history) diffset.save(update_fields=('revision', )) else: form_data = request.POST.copy() form = UploadDiffForm(review_request, data=form_data, files=request.FILES, request=request) if not form.is_valid(): return INVALID_FORM_DATA, { 'fields': self._get_form_errors(form), } try: diffset = form.create() except FileNotFoundError as e: return REPO_FILE_NOT_FOUND, { 'file': e.path, 'revision': six.text_type(e.revision) } except EmptyDiffError as e: return DIFF_EMPTY except DiffTooBigError as e: return DIFF_TOO_BIG, { 'reason': six.text_type(e), 'max_size': e.max_diff_size, } except Exception as e: # This could be very wrong, but at least they'll see the error. # We probably want a new error type for this. logger.error("Error uploading new diff: %s", e, exc_info=1, request=request) return INVALID_FORM_DATA, { 'fields': { 'path': [six.text_type(e)] } } discarded_diffset = None try: draft = review_request.draft.get() if draft.diffset and draft.diffset != diffset: discarded_diffset = draft.diffset except ReviewRequestDraft.DoesNotExist: try: draft = ReviewRequestDraftResource.prepare_draft( request, review_request) except PermissionDenied: return self.get_no_access_error(request) draft.diffset = diffset # We only want to add default reviewers the first time. Was bug 318. if review_request.can_add_default_reviewers(): draft.add_default_reviewers() draft.save() if extra_fields: try: self.import_extra_data(diffset, diffset.extra_data, extra_fields) except ImportExtraDataError as e: return e.error_payload diffset.save(update_fields=['extra_data']) if discarded_diffset: discarded_diffset.delete() # E-mail gets sent when the draft is saved. return 201, { self.item_result_key: diffset, }
def update(self, request, status=None, changenum=None, description=None, extra_fields={}, *args, **kwargs): """Updates the status of the review request. The only supported update to a review request's resource is to change the status, the associated server-side, change number, or to update information from the existing change number. The status can be set in order to close the review request as discarded or submitted, or to reopen as pending. For Perforce, a change number can either be changed to a new number, or the current change number can be passed. In either case, a new draft will be created or an existing one updated to include information from the server based on the change number. This behavior is deprecated, and instead, the commit_id field should be set on the draft. Changes to a review request's fields, such as the summary or the list of reviewers, is made on the Review Request Draft resource. This can be accessed through the ``draft`` link. Only when that draft is published will the changes end up back in this resource. Extra data can be stored on the review request for later lookup by passing ``extra_data.key_name=value``. The ``key_name`` and ``value`` can be any valid strings. Passing a blank ``value`` will remove the key. The ``extra_data.`` prefix is required. """ try: review_request = \ resources.review_request.get_object(request, *args, **kwargs) except ObjectDoesNotExist: return DOES_NOT_EXIST is_mutating_field = (changenum is not None or extra_fields) if ((is_mutating_field and not self.has_modify_permissions(request, review_request)) or (status is not None and not review_request.is_status_mutable_by(request.user))): return self._no_access_error(request.user) if (status is not None and (review_request.status != string_to_status(status) or review_request.status != ReviewRequest.PENDING_REVIEW)): try: if status in self._close_type_map: review_request.close(self._close_type_map[status], request.user, description) elif status == 'pending': review_request.reopen(request.user) else: raise AssertionError("Code path for invalid status '%s' " "should never be reached." % status) except PermissionError: return self._no_access_error(request.user) # Preserve the old changenum behavior. if changenum is not None: if changenum != review_request.changenum: review_request.commit = changenum try: draft = ReviewRequestDraftResource.prepare_draft( request, review_request) except PermissionDenied: return PERMISSION_DENIED try: draft.update_from_commit_id(six.text_type(changenum)) except InvalidChangeNumberError: return INVALID_CHANGE_NUMBER draft.save() review_request.reopen() if extra_fields: self._import_extra_data(review_request.extra_data, extra_fields) review_request.save(update_fields=['extra_data']) return 200, { self.item_result_key: review_request, }
return INVALID_FORM_DATA, { 'fields': { 'path': [str(e)] } } discarded_diffset = None try: draft = review_request.draft.get() if draft.diffset and draft.diffset != diffset: discarded_diffset = draft.diffset except ReviewRequestDraft.DoesNotExist: try: draft = ReviewRequestDraftResource.prepare_draft( request, review_request) except PermissionDenied: return self._no_access_error(request.user) draft.diffset = diffset # We only want to add default reviewers the first time. Was bug 318. if review_request.diffset_history.diffsets.count() == 0: draft.add_default_reviewers() draft.save() if discarded_diffset: discarded_diffset.delete() # E-mail gets sent when the draft is saved.
e, exc_info=1, request=request) return INVALID_FORM_DATA, {'fields': {'path': [str(e)]}} discarded_diffset = None try: draft = review_request.draft.get() if draft.diffset and draft.diffset != diffset: discarded_diffset = draft.diffset except ReviewRequestDraft.DoesNotExist: try: draft = ReviewRequestDraftResource.prepare_draft( request, review_request) except PermissionDenied: return self._no_access_error(request.user) draft.diffset = diffset # We only want to add default reviewers the first time. Was bug 318. if review_request.diffset_history.diffsets.count() == 0: draft.add_default_reviewers() draft.save() if discarded_diffset: discarded_diffset.delete() # E-mail gets sent when the draft is saved.
def create(self, request, *args, **kwargs): """Creates a new diff by parsing an uploaded diff file. This will implicitly create the new Review Request draft, which can be updated separately and then published. This accepts a unified diff file, validates it, and stores it along with the draft of a review request. The new diff will have a revision of 0. A parent diff can be uploaded along with the main diff. A parent diff is a diff based on an existing commit in the repository, which will be applied before the main diff. The parent diff will not be included in the diff viewer. It's useful when developing a change based on a branch that is not yet committed. In this case, a parent diff of the parent branch would be provided along with the diff of the new commit, and only the new commit will be shown. It is expected that the client will send the data as part of a :mimetype:`multipart/form-data` mimetype. The main diff's name and content would be stored in the ``path`` field. If a parent diff is provided, its name and content would be stored in the ``parent_diff_path`` field. An example of this would be:: -- SoMe BoUnDaRy Content-Disposition: form-data; name=path; filename="foo.diff" <Unified Diff Content Here> -- SoMe BoUnDaRy -- """ # Prevent a circular dependency, as ReviewRequestDraftResource # needs DraftDiffResource, which needs DiffResource. from reviewboard.webapi.resources.review_request_draft import \ ReviewRequestDraftResource try: review_request = \ resources.review_request.get_object(request, *args, **kwargs) except ReviewRequest.DoesNotExist: return DOES_NOT_EXIST if not review_request.is_mutable_by(request.user): return self._no_access_error(request.user) form_data = request.POST.copy() form = UploadDiffForm(review_request, form_data, request.FILES, request=request) if not form.is_valid(): return INVALID_FORM_DATA, { 'fields': self._get_form_errors(form), } try: diffset = form.create(request.FILES['path'], request.FILES.get('parent_diff_path')) except FileNotFoundError as e: return REPO_FILE_NOT_FOUND, { 'file': e.path, 'revision': six.text_type(e.revision) } except EmptyDiffError as e: return DIFF_EMPTY except DiffTooBigError as e: return DIFF_TOO_BIG, { 'reason': six.text_type(e), 'max_size': e.max_diff_size, } except Exception as e: # This could be very wrong, but at least they'll see the error. # We probably want a new error type for this. logging.error("Error uploading new diff: %s", e, exc_info=1, request=request) return INVALID_FORM_DATA, {'fields': {'path': [six.text_type(e)]}} discarded_diffset = None try: draft = review_request.draft.get() if draft.diffset and draft.diffset != diffset: discarded_diffset = draft.diffset except ReviewRequestDraft.DoesNotExist: try: draft = ReviewRequestDraftResource.prepare_draft( request, review_request) except PermissionDenied: return self._no_access_error(request.user) draft.diffset = diffset # We only want to add default reviewers the first time. Was bug 318. if review_request.diffset_history.diffsets.count() == 0: draft.add_default_reviewers() draft.save() if discarded_diffset: discarded_diffset.delete() # E-mail gets sent when the draft is saved. return 201, { self.item_result_key: diffset, }
def create(self, request, extra_fields={}, *args, **kwargs): """Creates a new diff by parsing an uploaded diff file. This will implicitly create the new Review Request draft, which can be updated separately and then published. This accepts a unified diff file, validates it, and stores it along with the draft of a review request. The new diff will have a revision of 0. A parent diff can be uploaded along with the main diff. A parent diff is a diff based on an existing commit in the repository, which will be applied before the main diff. The parent diff will not be included in the diff viewer. It's useful when developing a change based on a branch that is not yet committed. In this case, a parent diff of the parent branch would be provided along with the diff of the new commit, and only the new commit will be shown. It is expected that the client will send the data as part of a :mimetype:`multipart/form-data` mimetype. The main diff's name and content would be stored in the ``path`` field. If a parent diff is provided, its name and content would be stored in the ``parent_diff_path`` field. An example of this would be:: -- SoMe BoUnDaRy Content-Disposition: form-data; name=path; filename="foo.diff" <Unified Diff Content Here> -- SoMe BoUnDaRy -- Extra data can be stored on the diff for later lookup by passing ``extra_data.key_name=value``. The ``key_name`` and ``value`` can be any valid strings. Passing a blank ``value`` will remove the key. The ``extra_data.`` prefix is required. """ # Prevent a circular dependency, as ReviewRequestDraftResource # needs DraftDiffResource, which needs DiffResource. from reviewboard.webapi.resources.review_request_draft import \ ReviewRequestDraftResource try: review_request = \ resources.review_request.get_object(request, *args, **kwargs) except ReviewRequest.DoesNotExist: return DOES_NOT_EXIST if not review_request.is_mutable_by(request.user): return self._no_access_error(request.user) form_data = request.POST.copy() form = UploadDiffForm(review_request, form_data, request.FILES, request=request) if not form.is_valid(): return INVALID_FORM_DATA, { 'fields': self._get_form_errors(form), } try: diffset = form.create(request.FILES['path'], request.FILES.get('parent_diff_path')) except FileNotFoundError as e: return REPO_FILE_NOT_FOUND, { 'file': e.path, 'revision': six.text_type(e.revision) } except EmptyDiffError as e: return DIFF_EMPTY except DiffTooBigError as e: return DIFF_TOO_BIG, { 'reason': six.text_type(e), 'max_size': e.max_diff_size, } except Exception as e: # This could be very wrong, but at least they'll see the error. # We probably want a new error type for this. logging.error("Error uploading new diff: %s", e, exc_info=1, request=request) return INVALID_FORM_DATA, { 'fields': { 'path': [six.text_type(e)] } } discarded_diffset = None try: draft = review_request.draft.get() if draft.diffset and draft.diffset != diffset: discarded_diffset = draft.diffset except ReviewRequestDraft.DoesNotExist: try: draft = ReviewRequestDraftResource.prepare_draft( request, review_request) except PermissionDenied: return self._no_access_error(request.user) draft.diffset = diffset # We only want to add default reviewers the first time. Was bug 318. if review_request.diffset_history.diffsets.count() == 0: draft.add_default_reviewers() draft.save() if extra_fields: self._import_extra_data(diffset.extra_data, extra_fields) diffset.save(update_fields=['extra_data']) if discarded_diffset: discarded_diffset.delete() # E-mail gets sent when the draft is saved. return 201, { self.item_result_key: diffset, }