PageRenderTime 57ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/reviewboard/webapi/resources/diffcommit.py

https://github.com/pombredanne/reviewboard
Python | 393 lines | 272 code | 23 blank | 98 comment | 7 complexity | 7b89459c79945f67b16a69dcc5209e6a MD5 | raw file
  1. """Resource representing the commits in a multi-commit review request."""
  2. from urllib.parse import urlencode
  3. from django.core.exceptions import ObjectDoesNotExist
  4. from django.http import HttpResponse
  5. from djblets.util.decorators import augment_method_from
  6. from djblets.util.http import get_http_requested_mimetype, set_last_modified
  7. from djblets.webapi.decorators import webapi_request_fields
  8. from djblets.webapi.errors import (DOES_NOT_EXIST, NOT_LOGGED_IN,
  9. PERMISSION_DENIED)
  10. from djblets.webapi.fields import (DateTimeFieldType, DictFieldType,
  11. IntFieldType, StringFieldType)
  12. from reviewboard.diffviewer.features import dvcs_feature
  13. from reviewboard.diffviewer.models import DiffCommit, DiffSet
  14. from reviewboard.diffviewer.validators import COMMIT_ID_LENGTH
  15. from reviewboard.webapi.base import ImportExtraDataError, WebAPIResource
  16. from reviewboard.webapi.decorators import (webapi_check_local_site,
  17. webapi_check_login_required,
  18. webapi_login_required,
  19. webapi_response_errors)
  20. from reviewboard.webapi.resources import resources
  21. class DiffCommitResource(WebAPIResource):
  22. """Provides information on a collection of commits in a review request.
  23. Each diff commit resource contains individual per-file diffs as child
  24. resources, as well as metadata to reproduce the actual commits in a
  25. version control system.
  26. """
  27. added_in = '4.0'
  28. model = DiffCommit
  29. name = 'commit'
  30. model_parent_key = 'diffset'
  31. model_object_key = 'commit_id'
  32. uri_object_key = 'commit_id'
  33. uri_object_key_regex = r'[A-Za-z0-9]{1,%s}' % COMMIT_ID_LENGTH
  34. allowed_methods = ('GET', 'PUT')
  35. required_features = [dvcs_feature]
  36. allowed_mimetypes = WebAPIResource.allowed_mimetypes + [
  37. {'item': 'text/x-patch'},
  38. ]
  39. fields = {
  40. 'id': {
  41. 'type': IntFieldType,
  42. 'description': 'The numeric ID of the commit resource.',
  43. },
  44. 'author_name': {
  45. 'type': StringFieldType,
  46. 'description': 'The name of the author of this commit.',
  47. },
  48. 'author_date': {
  49. 'type': DateTimeFieldType,
  50. 'description': 'The date and time this commit was authored in ISO '
  51. '8601 format (YYYY-MM-DD HH:MM:SS+ZZZZ).',
  52. },
  53. 'author_email': {
  54. 'type': StringFieldType,
  55. 'description': 'The e-mail address of the author of this commit.',
  56. },
  57. 'commit_id': {
  58. 'type': StringFieldType,
  59. 'description': 'The ID of this commit.',
  60. },
  61. 'committer_name': {
  62. 'type': StringFieldType,
  63. 'description': 'The name of the the committer of this commit, if '
  64. 'applicable.',
  65. },
  66. 'committer_date': {
  67. 'type': StringFieldType,
  68. 'description': 'The date and time this commit was committed in '
  69. 'ISO 8601 format (YYYY-MM-DD HH:MM:SS+ZZZZ).',
  70. },
  71. 'committer_email': {
  72. 'type': StringFieldType,
  73. 'description': 'The e-mail address of the committer of this '
  74. 'commit.',
  75. },
  76. 'commit_message': {
  77. 'type': StringFieldType,
  78. 'description': 'The commit message.',
  79. },
  80. 'extra_data': {
  81. 'type': DictFieldType,
  82. 'description': 'Extra data as part of the commit. This can be set '
  83. 'by the API or extensions.',
  84. },
  85. 'filename': {
  86. 'type': StringFieldType,
  87. 'description': 'The name of the corresponding diff.',
  88. },
  89. 'parent_id': {
  90. 'type': StringFieldType,
  91. 'description': 'The ID of the parent commit.',
  92. },
  93. }
  94. def get_queryset(self, request, *args, **kwargs):
  95. """Return the queryset for the available commits.
  96. Args:
  97. request (django.http.HttpRequest):
  98. The HTTP request from the client.
  99. *args (tuple):
  100. Additional positional arguments.
  101. **kwargs (dict):
  102. Additional keyword arguments.
  103. Returns:
  104. django.db.models.query.QuerySet:
  105. The queryset of all available commits.
  106. """
  107. try:
  108. diffset = resources.diff.get_object(request, *args, **kwargs)
  109. except DiffSet.DoesNotExist:
  110. return self.model.objects.none()
  111. return self.model.objects.filter(diffset=diffset)
  112. def has_access_permissions(self, request, commit, *args, **kwargs):
  113. """Return whether or not the user has access permissions to the commit.
  114. A user has access permissions for a commit if they have permission to
  115. access the parent review request.
  116. Args:
  117. request (django.http.HttpRequest):
  118. The current HTTP request.
  119. commit (reviewboard.diffviewer.models.diffcommit.DiffCommit):
  120. The object to check access permissions for.
  121. *args (tuple):
  122. Additional positional arguments.
  123. **kwargs (dict):
  124. Additional keyword arguments.
  125. Returns:
  126. bool:
  127. Whether or not the user has permission to access the commit.
  128. """
  129. review_request = resources.review_request.get_object(request, *args,
  130. **kwargs)
  131. return review_request.is_accessible_by(request.user)
  132. def has_list_access_permissions(self, request, *args, **kwargs):
  133. """Return whether the user has access permissions to the list resource.
  134. A user has list access permissions if they have premission to access
  135. the parent review request.
  136. Args:
  137. request (django.http.HttpRequest):
  138. The current HTTP request.
  139. commit (reviewboard.diffviewer.models.diffcommit.DiffCommit):
  140. The object to check access permissions for.
  141. *args (tuple):
  142. Additional positional arguments.
  143. **kwargs (dict):
  144. Additional keyword arguments.
  145. Returns:
  146. bool:
  147. Whether or not the user has permission to access the list resource.
  148. """
  149. review_request = resources.review_request.get_object(request, *args,
  150. **kwargs)
  151. return review_request.is_accessible_by(request.user)
  152. def has_modify_permissions(self, request, obj, *args, **kwargs):
  153. """Return whether the user has access permissions to modify the object.
  154. A user has modify permissions for a commit if they have permission to
  155. modify the parent review request.
  156. Args:
  157. request (django.http.HttpRequest):
  158. The current HTTP request.
  159. commit (reviewboard.diffviewer.models.diffcommit.DiffCommit):
  160. The object to check access permissions for.
  161. *args (tuple):
  162. Additional positional arguments.
  163. **kwargs (dict):
  164. Additional keyword arguments.
  165. Returns:
  166. bool:
  167. Whether or not the user has permission to modify the object.
  168. """
  169. review_request = resources.review_request.get_object(request, *args,
  170. **kwargs)
  171. return review_request.is_mutable_by(request.user)
  172. @webapi_check_login_required
  173. @webapi_check_local_site
  174. @webapi_response_errors(DOES_NOT_EXIST)
  175. @augment_method_from(WebAPIResource)
  176. def get_list(self, request, *args, **kwargs):
  177. """Return the list of commits."""
  178. @webapi_check_login_required
  179. @webapi_check_local_site
  180. @webapi_response_errors(DOES_NOT_EXIST)
  181. def get(self, request, *args, **kwargs):
  182. """Return information about a commit.
  183. If the :mimetype:`text/x-patch` mimetype is requested, the contents of
  184. the patch will be returned.
  185. Otherwise, metadata about the commit (such as author name, author date,
  186. etc.) will be returned.
  187. """
  188. mimetype = get_http_requested_mimetype(
  189. request,
  190. [mimetype['item'] for mimetype in self.allowed_mimetypes])
  191. if mimetype != 'text/x-patch':
  192. return super(DiffCommitResource, self).get(request, *args,
  193. **kwargs)
  194. try:
  195. review_request = resources.review_request.get_object(
  196. request, *args, **kwargs)
  197. commit = self.get_object(request, *args, **kwargs)
  198. except ObjectDoesNotExist:
  199. return DOES_NOT_EXIST
  200. if not self.has_access_permissions(request, commit, *args, **kwargs):
  201. return self.get_no_access_error(request)
  202. tool = review_request.repository.get_scmtool()
  203. data = tool.get_parser(b'').raw_diff(commit)
  204. rsp = HttpResponse(data, content_type=mimetype)
  205. rsp['Content-Disposition'] = ('inline; filename=%s.patch'
  206. % commit.commit_id)
  207. set_last_modified(rsp, commit.last_modified)
  208. return rsp
  209. @webapi_login_required
  210. @webapi_check_local_site
  211. @webapi_response_errors(DOES_NOT_EXIST, NOT_LOGGED_IN, PERMISSION_DENIED)
  212. @webapi_request_fields(allow_unknown=True)
  213. def update(self, request, extra_fields=None, *args, **kwargs):
  214. """Update a commit.
  215. This is used solely for modifying the extra data on a commit. The
  216. contents of a commit cannot be modified.
  217. Extra data can be stored for later lookup. See
  218. :ref:`webapi2.0-extra-data` for more information.
  219. """
  220. try:
  221. commit = self.get_object(request, *args, **kwargs)
  222. except DiffCommit.DoesNotExist:
  223. return DOES_NOT_EXIST
  224. if not self.has_modify_permissions(request, commit, *args, **kwargs):
  225. return self.get_no_access_error(request)
  226. if extra_fields:
  227. try:
  228. self.import_extra_data(commit, commit.extra_data, extra_fields)
  229. except ImportExtraDataError as e:
  230. return e.error_payload
  231. commit.save(update_fields=['extra_data'])
  232. return 200, {
  233. self.item_result_key: commit,
  234. }
  235. def _get_files_link(self, commit, request, diff_resource,
  236. filediff_resource, files_key, *args, **kwargs):
  237. """Return the link for the files.
  238. As an alternative to having a per-commit files resources (and draft
  239. resource equivalent), this method generates link to the
  240. :py:class:`~reviewboard.webapi.resources.filediff.FileDiffResource`
  241. (or :py:class:`~reviewboard.webapi.resources.draft_filediff.
  242. DraftFileDiffResource` for commits on a review request draft) filtered
  243. specifically for this commit.
  244. Args:
  245. commit (reviewboard.diffviewer.models.diffcommit.DiffCommit):
  246. The commit to retrieve the link for.
  247. request (django.http.HttpRequest):
  248. The current HTTP request.
  249. diff_resource (reviewboard.webapi.resources.diff.DiffResource):
  250. Either the diff resource (if this is the diff commit resource)
  251. or the draft diff resource (if this is the draft diff commit
  252. resource).
  253. filediff_resource (reviewboard.webapi.resources.filediff.
  254. FileDiffResource):
  255. Either the filedif resource (if this is the diff commit
  256. resource) or the draft filediff resource (if this is the draft
  257. diff commit resource).
  258. files_key (unicode):
  259. The key that maps to the files link in the ``links`` field of
  260. the ``diff_resource``.
  261. *args (tuple):
  262. Additional positional argument.
  263. **kwargs (dict):
  264. Additional keyword arguments.
  265. Returns:
  266. dict:
  267. The link information for the file resource that contains only the
  268. :py:class:`FileDiffs
  269. <reviewboard.diffviewer.models.filediff.FileDiff>` of the
  270. requested commit.
  271. """
  272. files_link = diff_resource.get_links(
  273. [filediff_resource],
  274. commit.diffset,
  275. request,
  276. *args,
  277. **kwargs)[files_key]
  278. files_link['href'] = '%s?%s' % (
  279. files_link['href'],
  280. urlencode({'commit-id': commit.commit_id}),
  281. )
  282. return files_link
  283. def get_related_links(self, obj=None, request=None, *args, **kwargs):
  284. """Return the related links for the resource.
  285. If this is for an item resource, this will return links for all the
  286. associated
  287. :py:class:`~reviewboard.diffviewer.models.filediff.FileDiffs`.
  288. Args:
  289. obj (reviewboard.diffviewer.models.diffcommit.DiffCommit,
  290. optional):
  291. The DiffCommit to get links for.
  292. request (django.http.HttpRequest):
  293. The HTTP request from the client.
  294. *args (tuple):
  295. Additional positional arguments.
  296. **kwargs (dict):
  297. Additional keyword arguments.
  298. Returns:
  299. dict:
  300. The related links.
  301. """
  302. links = {}
  303. if obj is not None and request:
  304. links['files'] = self._get_files_link(
  305. commit=obj,
  306. request=request,
  307. diff_resource=resources.diff,
  308. filediff_resource=resources.filediff,
  309. files_key='files',
  310. *args,
  311. **kwargs)
  312. return links
  313. diffcommit_resource = DiffCommitResource()