diff --git a/CHANGELOG b/CHANGELOG index ae86734434f..47185789da5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,14 @@ We follow the CalVer (https://calver.org/) versioning scheme: YY.MINOR.MICRO. +19.27.0 (2019-09-18) +=================== +- Automatically map subjects when a preprint is moved to a different + preprint provider in the admin app +- Gitlab: return all repos to which the user has access +- Upgrade Bower +- Py3 backwards compatible changes + 19.26.0 (2019-09-11) =================== - Create a custom through table for linking files and versions diff --git a/README-docker-compose.md b/README-docker-compose.md index d84a2670014..be1f2326c5a 100644 --- a/README-docker-compose.md +++ b/README-docker-compose.md @@ -81,6 +81,9 @@ `$ cp ./docker-compose-dist.override.yml ./docker-compose.override.yml` + For local tasks, (dev only) + `$ cp ./tasks/local-dist.py ./tasks/local.py` + 2. OPTIONAL (uncomment the below lines if you will use remote debugging) Environment variables (incl. remote debugging) - e.g. .docker-compose.env diff --git a/addons/base/generic_views.py b/addons/base/generic_views.py index a00bd2a088f..001194fe29e 100644 --- a/addons/base/generic_views.py +++ b/addons/base/generic_views.py @@ -1,6 +1,6 @@ """Generic add-on view factories""" # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from flask import request @@ -28,12 +28,12 @@ def _import_auth(auth, node_addon, user_addon, **kwargs): ) if not user_addon.external_accounts.filter(id=external_account.id).exists(): - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) try: node_addon.set_auth(external_account, user_addon.owner) except PermissionsError: - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) node_addon.save() @@ -60,7 +60,7 @@ def folder_list(addon_short_name, addon_full_name, get_folders): def _folder_list(node_addon, **kwargs): """Returns a list of folders""" if not node_addon.has_auth: - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) folder_id = request.args.get('folderId') return get_folders(node_addon, folder_id) diff --git a/addons/base/tests/views.py b/addons/base/tests/views.py index 47d04e1ced3..8c3860b2826 100644 --- a/addons/base/tests/views.py +++ b/addons/base/tests/views.py @@ -1,5 +1,5 @@ -import httplib as http -import urlparse +from rest_framework import status as http_status +from future.moves.urllib.parse import urlparse, urljoin, parse_qs import mock import responses @@ -25,11 +25,11 @@ def test_oauth_start(self): service_name=self.ADDON_SHORT_NAME ) res = self.app.get(url, auth=self.user.auth) - assert res.status_code == http.FOUND - redirect_url = urlparse.urlparse(res.location) - redirect_params = urlparse.parse_qs(redirect_url.query) - provider_url = urlparse.urlparse(self.Provider().auth_url) - provider_params = urlparse.parse_qs(provider_url.query) + assert res.status_code == http_status.HTTP_302_FOUND + redirect_url = urlparse(res.location) + redirect_params = parse_qs(redirect_url.query) + provider_url = urlparse(self.Provider().auth_url) + provider_params = parse_qs(provider_url.query) for param, value in redirect_params.items(): if param == 'state': # state may change between calls continue @@ -43,7 +43,7 @@ def test_oauth_finish(self): with mock.patch.object(self.Provider, 'auth_callback') as mock_callback: mock_callback.return_value = True res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) name, args, kwargs = mock_callback.mock_calls[0] assert_equal(kwargs['user']._id, self.user._id) @@ -53,7 +53,7 @@ def test_delete_external_account(self): external_account_id=self.external_account._id ) res = self.app.delete(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.user.reload() for account in self.user.external_accounts.all(): assert_not_equal(account._id, self.external_account._id) @@ -66,7 +66,7 @@ def test_delete_external_account_not_owner(self): external_account_id=self.external_account._id ) res = self.app.delete(url, auth=other_user.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) class OAuthAddonConfigViewsTestCaseMixin(OAuthAddonTestCaseMixin): @@ -94,7 +94,7 @@ def test_import_auth(self): res = self.app.put_json(url, { 'external_account_id': ea._id }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('result', res.json) node_settings.reload() assert_equal(node_settings.external_account._id, ea._id) @@ -113,7 +113,7 @@ def test_import_auth_invalid_account(self): res = self.app.put_json(url, { 'external_account_id': ea._id }, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_import_auth_cant_write_node(self): ea = self.ExternalAccountFactory() @@ -130,7 +130,7 @@ def test_import_auth_cant_write_node(self): res = self.app.put_json(url, { 'external_account_id': ea._id }, auth=user.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_set_config(self): self.node_settings.set_auth(self.external_account, self.user) @@ -138,7 +138,7 @@ def test_set_config(self): res = self.app.put_json(url, { 'selected': self.folder }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() assert_equal( self.project.logs.latest().action, @@ -150,7 +150,7 @@ def test_get_config(self): url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) with mock.patch.object(type(self.Serializer()), 'credentials_are_valid', return_value=True): res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('result', res.json) serialized = self.Serializer().serialize_settings( self.node_settings, @@ -164,17 +164,17 @@ def test_get_config_unauthorized(self): user = AuthUserFactory() self.project.add_contributor(user, permissions=permissions.READ, auth=self.auth, save=True) res = self.app.get(url, auth=user.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_get_config_not_logged_in(self): url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=None, expect_errors=True) - assert_equal(res.status_code, http.FOUND) + assert_equal(res.status_code, http_status.HTTP_302_FOUND) def test_account_list_single(self): url = api_url_for('{0}_account_list'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('accounts', res.json) assert_equal(len(res.json['accounts']), 1) @@ -185,14 +185,14 @@ def test_account_list_multiple(self): url = api_url_for('{0}_account_list'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('accounts', res.json) assert_equal(len(res.json['accounts']), 2) def test_account_list_not_authorized(self): url = api_url_for('{0}_account_list'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=None, expect_errors=True) - assert_equal(res.status_code, http.FOUND) + assert_equal(res.status_code, http_status.HTTP_302_FOUND) def test_folder_list(self): # Note: if your addon's folder_list view makes API calls @@ -202,13 +202,13 @@ def test_folder_list(self): self.node_settings.save() url = self.project.api_url_for('{0}_folder_list'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) # TODO test result serialization? def test_deauthorize_node(self): url = self.project.api_url_for('{0}_deauthorize_node'.format(self.ADDON_SHORT_NAME)) res = self.app.delete(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.node_settings.reload() assert_is_none(self.node_settings.external_account) assert_false(self.node_settings.has_auth) @@ -256,7 +256,7 @@ def test_set_config(self): 'external_list_id': self.folder.json['id'], 'external_list_name': self.folder.name, }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() assert_equal( self.project.logs.latest().action, @@ -271,7 +271,7 @@ def test_get_config(self): self.node_settings.save() url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('result', res.json) result = res.json['result'] serialized = self.Serializer( @@ -287,7 +287,7 @@ def test_folder_list(self): self.node_settings.save() url = self.project.api_url_for('{0}_citation_list'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) def test_check_credentials(self): with mock.patch.object(self.client, 'client', new_callable=mock.PropertyMock) as mock_client: @@ -412,4 +412,4 @@ def test_citation_list_non_linked_or_child_non_authorizer(self): auth=non_authorizing_user.auth, expect_errors=True ) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) diff --git a/addons/base/views.py b/addons/base/views.py index 763aec065a8..44a83d2c4a0 100644 --- a/addons/base/views.py +++ b/addons/base/views.py @@ -1,9 +1,9 @@ import datetime -import httplib +from rest_framework import status as http_status import os import uuid import markupsafe -import urllib +from future.moves.urllib.parse import quote from django.utils import timezone from flask import make_response @@ -112,7 +112,7 @@ def disable_addon(auth, **kwargs): addon_name = kwargs.get('addon') if addon_name is None: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) deleted = node.delete_addon(addon_name, auth) @@ -126,11 +126,11 @@ def get_addon_user_config(**kwargs): addon_name = kwargs.get('addon') if addon_name is None: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) addon = user.get_addon(addon_name) if addon is None: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) return addon.to_json(user) @@ -158,7 +158,7 @@ def check_access(node, auth, action, cas_resp): """ permission = permission_map.get(action, None) if permission is None: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if cas_resp: if permission == permissions.READ: @@ -170,7 +170,7 @@ def check_access(node, auth, action, cas_resp): if not cas_resp.authenticated \ or required_scope not in oauth_scopes.normalize_scopes(cas_resp.attributes['accessTokenScope']): - raise HTTPError(httplib.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) if permission == permissions.READ: if node.can_view_files(auth): @@ -218,7 +218,7 @@ def check_access(node, auth, action, cas_resp): except RegistrationSchema.DoesNotExist: pass - raise HTTPError(httplib.FORBIDDEN if auth.user else httplib.UNAUTHORIZED) + raise HTTPError(http_status.HTTP_403_FORBIDDEN if auth.user else http_status.HTTP_401_UNAUTHORIZED) def make_auth(user): if user is not None: @@ -280,7 +280,7 @@ def get_auth(auth, **kwargs): )['data'] except (jwt.InvalidTokenError, KeyError) as err: sentry.log_message(str(err)) - raise HTTPError(httplib.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) if not auth.user: auth.user = OSFUser.from_cookie(data.get('cookie', '')) @@ -290,18 +290,18 @@ def get_auth(auth, **kwargs): node_id = data['nid'] provider_name = data['provider'] except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) node = AbstractNode.load(node_id) or Preprint.load(node_id) if not node: - raise HTTPError(httplib.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) check_access(node, auth, action, cas_resp) provider_settings = None if hasattr(node, 'get_addon'): provider_settings = node.get_addon(provider_name) if not provider_settings: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) path = data.get('path') credentials = None @@ -321,7 +321,7 @@ def get_auth(auth, **kwargs): identifier=version ).select_related('region').get() except FileVersion.DoesNotExist: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if auth.user: # mark fileversion as seen FileVersionUserMetadata.objects.get_or_create(user=auth.user, file_version=fileversion) @@ -404,11 +404,11 @@ def create_waterbutler_log(payload, **kwargs): user = OSFUser.load(auth['id']) if user is None: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) action = LOG_ACTION_MAP[payload['action']] except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) auth = Auth(user=user) node = kwargs.get('node') or kwargs.get('project') or Preprint.load(kwargs.get('nid')) or Preprint.load(kwargs.get('pid')) @@ -418,7 +418,7 @@ def create_waterbutler_log(payload, **kwargs): for bundle in ('source', 'destination'): for key in ('provider', 'materialized', 'name', 'nid'): if key not in payload[bundle]: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) dest = payload['destination'] src = payload['source'] @@ -594,7 +594,7 @@ def addon_view_or_download_file_legacy(**kwargs): action=action, **query_params ), - code=httplib.MOVED_PERMANENTLY + code=http_status.HTTP_301_MOVED_PERMANENTLY ) @must_be_contributor_or_public @@ -674,7 +674,7 @@ def addon_deleted_file(auth, target, error_type='BLAME_PROVIDER', **kwargs): # TODO - serialize deleted metadata for future types of deleted file targets ret = {'error': error_msg} - return ret, httplib.GONE + return ret, http_status.HTTP_410_GONE @must_be_contributor_or_public @@ -691,7 +691,7 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): path_safe = markupsafe.escape(path) if not path: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if hasattr(target, 'get_addon'): @@ -699,19 +699,19 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): if not isinstance(node_addon, BaseStorageAddon): object_text = markupsafe.escape(getattr(target, 'project_or_component', 'this object')) - raise HTTPError(httplib.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'The {} add-on containing {} is no longer connected to {}.'.format(provider_safe, path_safe, object_text) }) if not node_addon.has_auth: - raise HTTPError(httplib.UNAUTHORIZED, data={ + raise HTTPError(http_status.HTTP_401_UNAUTHORIZED, data={ 'message_short': 'Unauthorized', 'message_long': 'The {} add-on containing {} is no longer authorized.'.format(provider_safe, path_safe) }) if not node_addon.complete: - raise HTTPError(httplib.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'The {} add-on containing {} is no longer configured.'.format(provider_safe, path_safe) }) @@ -737,13 +737,13 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): file_node = BaseFileNode.load(path) if not file_node: - raise HTTPError(httplib.NOT_FOUND, data={ + raise HTTPError(http_status.HTTP_404_NOT_FOUND, data={ 'message_short': 'File Not Found', 'message_long': 'The requested file could not be found.' }) if file_node.kind == 'folder': - raise HTTPError(httplib.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'You cannot request a folder from this endpoint.' }) @@ -759,7 +759,7 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): # TODO clean up these urls and unify what is used as a version identifier if request.method == 'HEAD': - return make_response(('', httplib.FOUND, { + return make_response(('', http_status.HTTP_302_FOUND, { 'Location': file_node.generate_waterbutler_url(**dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render')) })) @@ -768,7 +768,7 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): _, extension = os.path.splitext(file_node.name) # avoid rendering files with the same format type. if format and '.{}'.format(format.lower()) != extension.lower(): - return redirect('{}/export?format={}&url={}'.format(get_mfr_url(target, provider), format, urllib.quote(file_node.generate_waterbutler_url( + return redirect('{}/export?format={}&url={}'.format(get_mfr_url(target, provider), format, quote(file_node.generate_waterbutler_url( **dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render') )))) return redirect(file_node.generate_waterbutler_url(**dict(extras, direct=None, version=version.identifier, _internal=extras.get('mode') == 'render'))) @@ -777,7 +777,7 @@ def addon_view_or_download_file(auth, path, provider, **kwargs): draft_id = extras.get('draft') draft = DraftRegistration.load(draft_id) if draft is None or draft.is_approved: - raise HTTPError(httplib.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Bad Request', 'message_long': 'File not associated with required object.' }) @@ -804,12 +804,12 @@ def persistent_file_download(auth, **kwargs): if guid: file = guid.referent else: - raise HTTPError(httplib.NOT_FOUND, data={ + raise HTTPError(http_status.HTTP_404_NOT_FOUND, data={ 'message_short': 'File Not Found', 'message_long': 'The requested file could not be found.' }) if not file.is_file: - raise HTTPError(httplib.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_long': 'Downloading folders is not permitted.' }) @@ -823,7 +823,7 @@ def persistent_file_download(auth, **kwargs): return redirect( file.generate_waterbutler_url(**query_params), - code=httplib.FOUND + code=http_status.HTTP_302_FOUND ) @@ -831,7 +831,7 @@ def addon_view_or_download_quickfile(**kwargs): fid = kwargs.get('fid', 'NOT_AN_FID') file_ = OsfStorageFile.load(fid) if not file_: - raise HTTPError(httplib.NOT_FOUND, data={ + raise HTTPError(http_status.HTTP_404_NOT_FOUND, data={ 'message_short': 'File Not Found', 'message_long': 'The requested file could not be found.' }) diff --git a/addons/bitbucket/api.py b/addons/bitbucket/api.py index b74d3cd6023..4a893ad991c 100644 --- a/addons/bitbucket/api.py +++ b/addons/bitbucket/api.py @@ -1,4 +1,4 @@ -import urllib +from future.moves.urllib.parse import urlencode from addons.bitbucket import settings @@ -184,7 +184,7 @@ def branches(self, user, repo): def ref_to_params(branch=None, sha=None): - params = urllib.urlencode({ + params = urlencode({ key: value for key, value in {'branch': branch, 'sha': sha}.items() if value diff --git a/addons/bitbucket/tests/test_views.py b/addons/bitbucket/tests/test_views.py index 6320799662b..b351bbe641d 100644 --- a/addons/bitbucket/tests/test_views.py +++ b/addons/bitbucket/tests/test_views.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status import mock import datetime @@ -68,7 +68,7 @@ def test_set_config(self, mock_account, mock_repo): 'bitbucket_user': 'octocat', 'bitbucket_repo': 'repo_name', }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() assert_equal( self.project.logs.latest().action, diff --git a/addons/bitbucket/utils.py b/addons/bitbucket/utils.py index 686973446bc..a7f8b213941 100644 --- a/addons/bitbucket/utils.py +++ b/addons/bitbucket/utils.py @@ -1,5 +1,5 @@ -import urllib -import httplib as http +from future.moves.urllib.parse import unquote_plus +from rest_framework import status as http_status from framework.exceptions import HTTPError @@ -9,9 +9,9 @@ def get_path(kwargs, required=True): path = kwargs.get('path') if path: - return urllib.unquote_plus(path) + return unquote_plus(path) elif required: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) def get_refs(addon, branch=None, sha=None, connection=None): @@ -26,7 +26,7 @@ def get_refs(addon, branch=None, sha=None, connection=None): connection = connection or BitbucketClient(access_token=addon.external_account.oauth_key) if sha and not branch: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Get default branch if not provided if not branch: diff --git a/addons/bitbucket/views.py b/addons/bitbucket/views.py index 14248b12a1a..df555703c34 100644 --- a/addons/bitbucket/views.py +++ b/addons/bitbucket/views.py @@ -1,6 +1,6 @@ """Views for the node settings page.""" # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status import logging from flask import request, make_response @@ -78,14 +78,14 @@ def bitbucket_set_config(auth, **kwargs): if not user_settings: user_settings = node_settings.user_settings except AttributeError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Parse request bitbucket_user_name = request.json.get('bitbucket_user', '') bitbucket_repo_name = request.json.get('bitbucket_repo', '') if not bitbucket_user_name or not bitbucket_repo_name: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Verify that repo exists and that user can access connection = BitbucketClient(access_token=node_settings.external_account.oauth_key) @@ -100,7 +100,7 @@ def bitbucket_set_config(auth, **kwargs): message = ( 'Cannot access repo.' ) - return {'message': message}, http.BAD_REQUEST + return {'message': message}, http_status.HTTP_400_BAD_REQUEST changed = ( bitbucket_user_name != node_settings.user or diff --git a/addons/box/models.py b/addons/box/models.py index cedb9a1ab9a..0ffb8de16c9 100644 --- a/addons/box/models.py +++ b/addons/box/models.py @@ -1,4 +1,4 @@ -import httplib as http +from rest_framework import status as http_status import logging import os @@ -144,14 +144,14 @@ def get_folders(self, **kwargs): oauth = OAuth2(client_id=settings.BOX_KEY, client_secret=settings.BOX_SECRET, access_token=self.external_account.oauth_key) client = Client(oauth) except BoxAPIException: - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) try: metadata = client.folder(folder_id).get() except BoxAPIException: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) except MaxRetryError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) folder_path = '/'.join( [ diff --git a/addons/box/tests/test_views.py b/addons/box/tests/test_views.py index 71cbb3c9e22..af7d646ae77 100644 --- a/addons/box/tests/test_views.py +++ b/addons/box/tests/test_views.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Views tests for the Box addon.""" from django.utils import timezone -import httplib +from rest_framework import status as http_status from nose.tools import * # noqa (PEP8 asserts) import mock import pytest @@ -140,7 +140,7 @@ def test_box_list_folders_returns_error_if_invalid_path(self, mock_metadata): mock_metadata.side_effect = BoxAPIException(status=404, message='File not found') url = self.project.api_url_for('box_folder_list', folder_id='lolwut') res = self.app.get(url, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, httplib.NOT_FOUND) + assert_equal(res.status_code, http_status.HTTP_404_NOT_FOUND) @mock.patch('addons.box.models.Client.folder') def test_box_list_folders_handles_max_retry_error(self, mock_metadata): @@ -148,7 +148,7 @@ def test_box_list_folders_handles_max_retry_error(self, mock_metadata): url = self.project.api_url_for('box_folder_list', folder_id='fo') mock_metadata.side_effect = MaxRetryError(mock_response, url) res = self.app.get(url, auth=self.user.auth, expect_errors=True) - assert_equal(res.status_code, httplib.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) class TestRestrictions(BoxAddonTestCase, OsfTestCase): @@ -180,13 +180,13 @@ def test_restricted_hgrid_data_contents(self, mock_auth): url = self.project.api_url_for('box_folder_list', path='foo bar') res = self.app.get(url, auth=self.contrib.auth, expect_errors=True) - assert_equal(res.status_code, httplib.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_restricted_config_contrib_no_addon(self): url = api_url_for('box_set_config', pid=self.project._primary_key) res = self.app.put_json(url, {'selected': {'path': 'foo'}}, auth=self.contrib.auth, expect_errors=True) - assert_equal(res.status_code, httplib.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) def test_restricted_config_contrib_not_owner(self): # Contributor has box auth, but is not the node authorizer @@ -196,4 +196,4 @@ def test_restricted_config_contrib_not_owner(self): url = api_url_for('box_set_config', pid=self.project._primary_key) res = self.app.put_json(url, {'selected': {'path': 'foo'}}, auth=self.contrib.auth, expect_errors=True) - assert_equal(res.status_code, httplib.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) diff --git a/addons/dataverse/client.py b/addons/dataverse/client.py index 7e1e95eef8c..d8b2362d875 100644 --- a/addons/dataverse/client.py +++ b/addons/dataverse/client.py @@ -1,4 +1,4 @@ -import httplib as http +from rest_framework import status as http_status from dataverse import Connection from dataverse.exceptions import ConnectionError, UnauthorizedError, OperationFailedError @@ -32,10 +32,10 @@ def connect_or_error(host, token): try: connection = _connect(host, token) if not connection: - raise HTTPError(http.SERVICE_UNAVAILABLE) + raise HTTPError(http_status.HTTP_503_SERVICE_UNAVAILABLE) return connection except UnauthorizedError: - raise HTTPError(http.UNAUTHORIZED) + raise HTTPError(http_status.HTTP_401_UNAUTHORIZED) def connect_from_settings_or_401(node_settings): @@ -57,17 +57,17 @@ def publish_dataverse(dataverse): try: dataverse.publish() except OperationFailedError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) def publish_dataset(dataset): if dataset.get_state() == 'RELEASED': - raise HTTPError(http.CONFLICT, data=dict( + raise HTTPError(http_status.HTTP_409_CONFLICT, data=dict( message_short='Dataset conflict', message_long='This version of the dataset has already been published.' )) if not dataset.dataverse.is_published: - raise HTTPError(http.METHOD_NOT_ALLOWED, data=dict( + raise HTTPError(http_status.HTTP_405_METHOD_NOT_ALLOWED, data=dict( message_short='Method not allowed', message_long='A dataset cannot be published until its parent Dataverse is published.' )) @@ -75,7 +75,7 @@ def publish_dataset(dataset): try: dataset.publish() except OperationFailedError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) def get_datasets(dataverse): @@ -90,13 +90,13 @@ def get_dataset(dataverse, doi): dataset = dataverse.get_dataset_by_doi(doi, timeout=settings.REQUEST_TIMEOUT) try: if dataset and dataset.get_state() == 'DEACCESSIONED': - raise HTTPError(http.GONE, data=dict( + raise HTTPError(http_status.HTTP_410_GONE, data=dict( message_short='Dataset deaccessioned', message_long='This dataset has been deaccessioned and can no longer be linked to the OSF.' )) return dataset except UnicodeDecodeError: - raise HTTPError(http.NOT_ACCEPTABLE, data=dict( + raise HTTPError(http_status.HTTP_406_NOT_ACCEPTABLE, data=dict( message_short='Not acceptable', message_long='This dataset cannot be connected due to forbidden ' 'characters in one or more of the file names.' diff --git a/addons/dataverse/models.py b/addons/dataverse/models.py index 1b327aefbbe..147eddc1fff 100644 --- a/addons/dataverse/models.py +++ b/addons/dataverse/models.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from addons.base.models import (BaseOAuthNodeSettings, BaseOAuthUserSettings, BaseStorageAddon) @@ -152,7 +152,7 @@ def _get_fileobj_child_metadata(self, filenode, user, cookie=None, version=None) return super(NodeSettings, self)._get_fileobj_child_metadata(filenode, user, cookie=cookie, version=version) except HTTPError as e: # The Dataverse API returns a 404 if the dataset has no published files - if e.code == http.NOT_FOUND and version == 'latest-published': + if e.code == http_status.HTTP_404_NOT_FOUND and version == 'latest-published': return [] raise diff --git a/addons/dataverse/tests/test_views.py b/addons/dataverse/tests/test_views.py index ae8a6cc6e89..bcf7e6ef1ca 100644 --- a/addons/dataverse/tests/test_views.py +++ b/addons/dataverse/tests/test_views.py @@ -4,7 +4,7 @@ import pytest import unittest -import httplib as http +from rest_framework import status as http_status from addons.base.tests.views import OAuthAddonConfigViewsTestCaseMixin @@ -98,7 +98,7 @@ def test_set_config(self, mock_connection): 'dataverse': {'alias': 'ALIAS3'}, 'dataset': {'doi': 'doi:12.3456/DVN/00003'}, }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() assert_equal( self.project.logs.latest().action, @@ -111,7 +111,7 @@ def test_set_config(self, mock_connection): def test_get_config(self): url = self.project.api_url_for('{0}_get_config'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('result', res.json) serialized = self.Serializer().serialize_settings( self.node_settings, @@ -137,7 +137,7 @@ def test_set_config_no_dataset(self, mock_connection): self.node_settings.reload() # Old settings did not change - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) assert_equal(self.node_settings.dataverse_alias, 'ALIAS2') assert_equal(self.node_settings.dataset, 'Example (DVN/00001)') assert_equal(self.node_settings.dataset_doi, 'doi:12.3456/DVN/00001') @@ -303,4 +303,4 @@ def test_restricted_set_dataset_not_owner(self, mock_connection): } res = self.app.post_json(url, params, auth=self.contrib.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) diff --git a/addons/dataverse/views.py b/addons/dataverse/views.py index 4d171fc6c10..a3d5e24834f 100644 --- a/addons/dataverse/views.py +++ b/addons/dataverse/views.py @@ -1,6 +1,6 @@ """Views for the node settings page.""" # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from django.utils import timezone from django.core.exceptions import ValidationError @@ -69,7 +69,7 @@ def dataverse_user_config_get(auth, **kwargs): }, 'hosts': DEFAULT_HOSTS, }, - }, http.OK + }, http_status.HTTP_200_OK ## Config ## @@ -129,13 +129,13 @@ def dataverse_set_config(node_addon, auth, **kwargs): user = auth.user if user_settings and user_settings.owner != user: - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) alias = request.json.get('dataverse', {}).get('alias') doi = request.json.get('dataset', {}).get('doi') if doi is None or alias is None: - return HTTPError(http.BAD_REQUEST) + return HTTPError(http_status.HTTP_400_BAD_REQUEST) connection = client.connect_from_settings(node_addon) dataverse = client.get_dataverse(connection, alias) @@ -143,7 +143,7 @@ def dataverse_set_config(node_addon, auth, **kwargs): node_addon.set_folder(dataverse, dataset, auth) - return {'dataverse': dataverse.title, 'dataset': dataset.title}, http.OK + return {'dataverse': dataverse.title, 'dataset': dataset.title}, http_status.HTTP_200_OK @must_have_permission(WRITE) @@ -160,7 +160,7 @@ def dataverse_get_datasets(node_addon, **kwargs): 'alias': alias, # include alias to verify dataset container 'datasets': [{'title': dataset.title, 'doi': dataset.doi} for dataset in datasets], } - return ret, http.OK + return ret, http_status.HTTP_200_OK ## Crud ## @@ -196,7 +196,7 @@ def dataverse_publish_dataset(node_addon, auth, **kwargs): log_date=now, ) - return {'dataset': dataset.title}, http.OK + return {'dataset': dataset.title}, http_status.HTTP_200_OK ## HGRID ## @@ -299,7 +299,7 @@ def dataverse_get_widget_contents(node_addon, **kwargs): } if not node_addon.complete: - return {'data': data}, http.OK + return {'data': data}, http_status.HTTP_200_OK doi = node_addon.dataset_doi alias = node_addon.dataverse_alias @@ -309,7 +309,7 @@ def dataverse_get_widget_contents(node_addon, **kwargs): dataset = client.get_dataset(dataverse, doi) if dataset is None: - return {'data': data}, http.BAD_REQUEST + return {'data': data}, http_status.HTTP_400_BAD_REQUEST dataverse_host = node_addon.external_account.oauth_key dataverse_url = 'http://{0}/dataverse/{1}'.format(dataverse_host, alias) @@ -324,4 +324,4 @@ def dataverse_get_widget_contents(node_addon, **kwargs): 'datasetUrl': dataset_url, 'citation': dataset.citation, }) - return {'data': data}, http.OK + return {'data': data}, http_status.HTTP_200_OK diff --git a/addons/dropbox/models.py b/addons/dropbox/models.py index 0fdc62e0e28..9bdce337b1a 100644 --- a/addons/dropbox/models.py +++ b/addons/dropbox/models.py @@ -1,4 +1,4 @@ -import httplib as http +from rest_framework import status as http_status import logging import os @@ -90,9 +90,9 @@ def auth_callback(self, user): # Either way, return and display info about how to properly connect. return except (oauth.ProviderException, oauth.CsrfException): - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) except oauth.BadRequestException: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) self.client = Dropbox(access_token) @@ -187,12 +187,12 @@ def get_folders(self, **kwargs): list_folder = client.files_list_folder_continue(list_folder.cursor) contents += [x for x in list_folder.entries] except ApiError as error: - raise HTTPError(http.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': error.user_message_text, 'message_long': error.user_message_text, }) except DropboxException: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) return [ { diff --git a/addons/dropbox/tests/test_views.py b/addons/dropbox/tests/test_views.py index f74a8db4d1d..4f76022e93d 100644 --- a/addons/dropbox/tests/test_views.py +++ b/addons/dropbox/tests/test_views.py @@ -1,5 +1,5 @@ """Views tests for the Dropbox addon.""" -import httplib as http +from rest_framework import status as http_status import unittest from dropbox.exceptions import ApiError @@ -160,7 +160,7 @@ def test_dropbox_folder_list_returns_error_if_invalid_path(self, mock_metadata): url = self.project.api_url_for('dropbox_folder_list', folder_id='/fake_path') with mock.patch.object(type(self.node_settings), 'has_auth', True): res = self.app.get(url, auth=self.user.auth, expect_errors=True) - assert res.status_code == http.BAD_REQUEST + assert res.status_code == http_status.HTTP_400_BAD_REQUEST class TestRestrictions(DropboxAddonTestCase, OsfTestCase): @@ -186,13 +186,13 @@ def test_restricted_folder_list(self, mock_metadata): url = self.project.api_url_for('dropbox_folder_list', path='foo bar') res = self.app.get(url, auth=self.contrib.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_restricted_config_contrib_no_addon(self): url = self.project.api_url_for('dropbox_set_config') res = self.app.put_json(url, {'selected': {'path': 'foo'}}, auth=self.contrib.auth, expect_errors=True) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) def test_restricted_config_contrib_not_owner(self): # Contributor has dropbox auth, but is not the node authorizer @@ -202,4 +202,4 @@ def test_restricted_config_contrib_not_owner(self): url = self.project.api_url_for('dropbox_set_config') res = self.app.put_json(url, {'selected': {'path': 'foo'}}, auth=self.contrib.auth, expect_errors=True) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) diff --git a/addons/figshare/tests/test_views.py b/addons/figshare/tests/test_views.py index 66a1d6dfb7f..d5e1a0c7787 100644 --- a/addons/figshare/tests/test_views.py +++ b/addons/figshare/tests/test_views.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # encoding: utf-8 -import httplib as http +from rest_framework import status as http_status import mock from nose.tools import assert_equal import pytest @@ -38,7 +38,7 @@ def test_set_config(self, mock_about): res = self.app.put_json(url, { 'selected': self.folder }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() assert_equal( self.project.logs.latest().action, diff --git a/addons/forward/views/config.py b/addons/forward/views/config.py index e3e21872e84..5385086d229 100644 --- a/addons/forward/views/config.py +++ b/addons/forward/views/config.py @@ -1,6 +1,6 @@ """Views for the node settings page.""" # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from flask import request from osf.exceptions import ValidationValueError @@ -39,14 +39,14 @@ def forward_config_put(auth, node_addon, **kwargs): node_addon.url = request.json['url'] node_addon.label = request.json.get('label') except (KeyError, TypeError, ValueError): - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Save settings and get changed fields; crash if validation fails try: dirty_fields = node_addon.get_dirty_fields() node_addon.save(request=request) except ValidationValueError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Log change if URL updated if 'url' in dirty_fields: diff --git a/addons/github/api.py b/addons/github/api.py index 60cbad74944..15286c9eed2 100644 --- a/addons/github/api.py +++ b/addons/github/api.py @@ -1,4 +1,4 @@ -import urllib +from future.moves.urllib.parse import urlencode import github3 import cachecontrol @@ -156,7 +156,7 @@ def check_authorization(self): def ref_to_params(branch=None, sha=None): - params = urllib.urlencode({ + params = urlencode({ key: value for key, value in { 'branch': branch, diff --git a/addons/github/models.py b/addons/github/models.py index e4dd5bfbccc..504cd23141e 100644 --- a/addons/github/models.py +++ b/addons/github/models.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import os -import urlparse +from future.moves.urllib.parse import urljoin import markupsafe from addons.base.models import (BaseOAuthNodeSettings, BaseOAuthUserSettings, @@ -450,7 +450,7 @@ def add_hook(self, save=True): self.user, self.repo, 'web', { - 'url': urlparse.urljoin( + 'url': urljoin( hook_domain, os.path.join( self.owner.api_url, 'github', 'hook/' diff --git a/addons/github/tests/test_views.py b/addons/github/tests/test_views.py index 3ebf7e438a2..868cf0282d3 100644 --- a/addons/github/tests/test_views.py +++ b/addons/github/tests/test_views.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status import unittest from django.utils import timezone @@ -69,7 +69,7 @@ def test_set_config(self, mock_repo, mock_add_hook): 'github_user': 'octocat', 'github_repo': 'repo_name', }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() assert_equal( self.project.logs.latest().action, diff --git a/addons/github/utils.py b/addons/github/utils.py index cb621396f81..c09ede234f5 100644 --- a/addons/github/utils.py +++ b/addons/github/utils.py @@ -1,8 +1,8 @@ import hmac import uuid -import urllib +from future.moves.urllib.parse import unquote_plus import hashlib -import httplib as http +from rest_framework import status as http_status from github3.repos.branch import Branch from framework.exceptions import HTTPError @@ -47,9 +47,9 @@ def verify_hook_signature(node_settings, data, headers): def get_path(kwargs, required=True): path = kwargs.get('path') if path: - return urllib.unquote_plus(path) + return unquote_plus(path) elif required: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) def get_refs(addon, branch=None, sha=None, connection=None): @@ -64,7 +64,7 @@ def get_refs(addon, branch=None, sha=None, connection=None): connection = connection or GitHubClient(external_account=addon.external_account) if sha and not branch: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Get default branch if not provided if not branch: @@ -85,7 +85,7 @@ def get_refs(addon, branch=None, sha=None, connection=None): ] # Fail if registered and branch not in registration data if registered_branches and branch not in registered_branch_names: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Get data from GitHub API if not registered branches = registered_branches or connection.branches(addon.user, addon.repo) diff --git a/addons/github/views.py b/addons/github/views.py index ab14f56bc98..fee04373a7d 100644 --- a/addons/github/views.py +++ b/addons/github/views.py @@ -1,7 +1,7 @@ """Views for the node settings page.""" # -*- coding: utf-8 -*- from dateutil.parser import parse as dateparse -import httplib as http +from rest_framework import status as http_status import logging from flask import request, make_response @@ -74,14 +74,14 @@ def github_set_config(auth, **kwargs): if not user_settings: user_settings = node_settings.user_settings except AttributeError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Parse request github_user_name = request.json.get('github_user', '') github_repo_name = request.json.get('github_repo', '') if not github_user_name or not github_repo_name: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Verify that repo exists and that user can access connection = GitHubClient(external_account=node_settings.external_account) @@ -96,7 +96,7 @@ def github_set_config(auth, **kwargs): message = ( 'Cannot access repo.' ) - return {'message': message}, http.BAD_REQUEST + return {'message': message}, http_status.HTTP_400_BAD_REQUEST changed = ( github_user_name != node_settings.user or @@ -191,7 +191,7 @@ def github_folder_list(node_addon, **kwargs): def github_create_repo(**kwargs): repo_name = request.json.get('name') if not repo_name: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) node_settings = kwargs['node_addon'] connection = GitHubClient(external_account=node_settings.external_account) @@ -200,7 +200,7 @@ def github_create_repo(**kwargs): repo = connection.create_repo(repo_name, auto_init=True) except GitHubError: # TODO: Check status code - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) return { 'user': repo.owner.login, diff --git a/addons/gitlab/api.py b/addons/gitlab/api.py index b9cb1da707a..b9a356d35d7 100644 --- a/addons/gitlab/api.py +++ b/addons/gitlab/api.py @@ -1,4 +1,4 @@ -import urllib +from future.moves.urllib.parse import urlencode import requests import gitlab @@ -55,7 +55,7 @@ def repo(self, repo_id): raise exc def repos(self): - return self.user().projects.list() + return self.gitlab.projects.list(membership=True) def branches(self, repo_id, branch=None): """List a repo's branches or get a single branch (in a list). @@ -132,7 +132,7 @@ def revoke_token(self): def ref_to_params(branch=None, sha=None): - params = urllib.urlencode({ + params = urlencode({ key: value for key, value in { 'branch': branch, diff --git a/addons/gitlab/models.py b/addons/gitlab/models.py index f812896e26b..bdc83d9758e 100644 --- a/addons/gitlab/models.py +++ b/addons/gitlab/models.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import os -import urlparse +from future.moves.urllib.parse import urljoin from django.db import models import markupsafe @@ -413,7 +413,7 @@ def add_hook(self, save=True): self.user, self.repo, 'web', { - 'url': urlparse.urljoin( + 'url': urljoin( hook_domain, os.path.join( self.owner.api_url, 'gitlab', 'hook/' diff --git a/addons/gitlab/tests/test_views.py b/addons/gitlab/tests/test_views.py index 81e941179d7..04ae8f73e64 100644 --- a/addons/gitlab/tests/test_views.py +++ b/addons/gitlab/tests/test_views.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status import mock import datetime @@ -76,7 +76,7 @@ def test_set_config(self, mock_repo, mock_add_hook): 'gitlab_repo': 'repo_name', 'gitlab_repo_id': '123', }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() assert_equal( self.project.logs.latest().action, diff --git a/addons/gitlab/utils.py b/addons/gitlab/utils.py index 157fb86de35..e5b398b7e47 100644 --- a/addons/gitlab/utils.py +++ b/addons/gitlab/utils.py @@ -1,8 +1,8 @@ import hmac import uuid -import urllib +from future.moves.urllib.parse import unquote_plus import hashlib -import httplib as http +from rest_framework import status as http_status from framework.exceptions import HTTPError from addons.base.exceptions import HookError @@ -46,9 +46,9 @@ def verify_hook_signature(node_settings, data, headers): def get_path(kwargs, required=True): path = kwargs.get('path') if path: - return urllib.unquote_plus(path) + return unquote_plus(path) elif required: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) def get_refs(addon, branch=None, sha=None, connection=None): @@ -63,7 +63,7 @@ def get_refs(addon, branch=None, sha=None, connection=None): connection = connection or GitLabClient(external_account=addon.external_account) if sha and not branch: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Get default branch if not provided if not branch: diff --git a/addons/gitlab/views.py b/addons/gitlab/views.py index b0b2adb1a4e..999cd343dc0 100644 --- a/addons/gitlab/views.py +++ b/addons/gitlab/views.py @@ -1,7 +1,7 @@ """Views for the node settings page.""" # -*- coding: utf-8 -*- from dateutil.parser import parse as dateparse -import httplib as http +from rest_framework import status as http_status import logging import gitlab @@ -84,7 +84,7 @@ def gitlab_user_config_get(auth, **kwargs): }, 'hosts': DEFAULT_HOSTS, }, - }, http.OK + }, http_status.HTTP_200_OK @must_be_logged_in def gitlab_add_user_account(auth, **kwargs): @@ -145,7 +145,7 @@ def gitlab_set_config(auth, **kwargs): if not user_settings: user_settings = node_settings.user_settings except AttributeError: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Parse request gitlab_user_name = request.json.get('gitlab_user', '') @@ -153,7 +153,7 @@ def gitlab_set_config(auth, **kwargs): gitlab_repo_id = request.json.get('gitlab_repo_id', '') if not gitlab_user_name or not gitlab_repo_name or not gitlab_repo_id: - raise HTTPError(http.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) # Verify that repo exists and that user can access connection = GitLabClient(external_account=node_settings.external_account) @@ -163,7 +163,7 @@ def gitlab_set_config(auth, **kwargs): except gitlab.exceptions.GitlabError as exc: if exc.response_code == 403 and 'must accept the Terms of Service' in exc.error_message: return {'message': 'Your gitlab account does not have proper authentication. Ensure you have agreed to Gitlab\'s ' - 'current Terms of Service by disabling and re-enabling your account.'}, http.BAD_REQUEST + 'current Terms of Service by disabling and re-enabling your account.'}, http_status.HTTP_400_BAD_REQUEST if repo is None: if user_settings: @@ -175,7 +175,7 @@ def gitlab_set_config(auth, **kwargs): message = ( 'Cannot access repo.' ) - return {'message': message}, http.BAD_REQUEST + return {'message': message}, http_status.HTTP_400_BAD_REQUEST changed = ( gitlab_user_name != node_settings.user or diff --git a/addons/mendeley/tests/test_views.py b/addons/mendeley/tests/test_views.py index 5e23498c5a0..df2bb2baed8 100644 --- a/addons/mendeley/tests/test_views.py +++ b/addons/mendeley/tests/test_views.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import mock import pytest -import urlparse +from future.moves.urllib.parse import urlparse, urljoin from addons.base.tests import views from addons.base.tests.utils import MockFolder @@ -24,8 +24,8 @@ class TestConfigViews(MendeleyTestCase, views.OAuthCitationAddonConfigViewsTestC Serializer = MendeleySerializer client = Mendeley citationsProvider = MendeleyCitationsProvider - foldersApiUrl = urlparse.urljoin(API_URL, 'folders') - documentsApiUrl = urlparse.urljoin(API_URL, 'documents') + foldersApiUrl = urljoin(API_URL, 'folders') + documentsApiUrl = urljoin(API_URL, 'documents') mockResponses = mock_responses @mock.patch('addons.mendeley.models.NodeSettings._fetch_folder_name', mock.PropertyMock(return_value='Fake Name')) diff --git a/addons/onedrive/models.py b/addons/onedrive/models.py index 033a8f4947c..ce256837bb7 100644 --- a/addons/onedrive/models.py +++ b/addons/onedrive/models.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import os -import urllib +from future.moves.urllib.parse import unquote import logging from django.db import models @@ -124,7 +124,7 @@ def folder_name(self): if self.folder_id != DEFAULT_ROOT_ID: # `urllib` does not properly handle unicode. # encode input to `str`, decode output back to `unicode` - return urllib.unquote(os.path.split(self.folder_path)[1].encode('utf-8')).decode('utf-8') + return unquote(os.path.split(self.folder_path)[1].encode('utf-8')).decode('utf-8') else: return '/ (Full OneDrive)' diff --git a/addons/osfstorage/decorators.py b/addons/osfstorage/decorators.py index 9493983d8ae..13727181e79 100644 --- a/addons/osfstorage/decorators.py +++ b/addons/osfstorage/decorators.py @@ -1,4 +1,4 @@ -import httplib +from rest_framework import status as http_status import functools from django.core.exceptions import ObjectDoesNotExist @@ -21,11 +21,11 @@ def wrapped(*args, **kwargs): try: return func(*args, **kwargs) except ObjectDoesNotExist: - raise HTTPError(httplib.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) except IntegrityError: - raise HTTPError(httplib.CONFLICT) + raise HTTPError(http_status.HTTP_409_CONFLICT) except exceptions.VersionNotFoundError: - raise HTTPError(httplib.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) return wrapped @@ -37,7 +37,7 @@ def wrapped(*args, **kwargs): target = getattr(Guid.load(guid), 'referent', None) if not target: raise HTTPError( - httplib.NOT_FOUND, + http_status.HTTP_404_NOT_FOUND, data={ 'message_short': 'Guid not resolved', 'message_long': 'No object with that guid could be found', @@ -63,7 +63,7 @@ def wrapped(*args, **kwargs): else: file_node = OsfStorageFileNode.get(kwargs.get('fid'), kwargs['target']) if must_be and file_node.kind != must_be: - raise HTTPError(httplib.BAD_REQUEST, data={ + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'incorrect type', 'message_long': 'FileNode must be of type {} not {}'.format(must_be, file_node.kind) }) @@ -99,7 +99,7 @@ def wrapped(payload, *args, **kwargs): 'name': payload['destination']['name'], }) except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) return func(*args, **kwargs) return wrapped diff --git a/addons/osfstorage/utils.py b/addons/osfstorage/utils.py index da2d995a0fa..055ac3d32e3 100644 --- a/addons/osfstorage/utils.py +++ b/addons/osfstorage/utils.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import os -import httplib +from rest_framework import status as http_status import logging import functools @@ -64,7 +64,7 @@ def serialize_revision(node, record, version, index, anon=False): SIGNED_REQUEST_ERROR = HTTPError( - httplib.SERVICE_UNAVAILABLE, + http_status.HTTP_503_SERVICE_UNAVAILABLE, data={ 'message_short': 'Upload service unavailable', 'message_long': ( diff --git a/addons/osfstorage/views.py b/addons/osfstorage/views.py index 8c5593946c9..b65657f420e 100644 --- a/addons/osfstorage/views.py +++ b/addons/osfstorage/views.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -import httplib +from rest_framework import status as http_status import logging from django.core.exceptions import ValidationError @@ -83,7 +83,7 @@ def osfstorage_update_metadata(payload, **kwargs): version_id = payload['version'] metadata = payload['metadata'] except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if check_select_for_update(): version = FileVersion.objects.filter(_id=version_id).select_for_update().first() @@ -91,7 +91,7 @@ def osfstorage_update_metadata(payload, **kwargs): version = FileVersion.objects.filter(_id=version_id).first() if version is None: - raise HTTPError(httplib.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) version.update_metadata(metadata) @@ -124,7 +124,7 @@ def osfstorage_get_revisions(file_node, payload, target, **kwargs): @decorators.waterbutler_opt_hook def osfstorage_copy_hook(source, destination, name=None, **kwargs): - ret = source.copy_under(destination, name=name).serialize(), httplib.CREATED + ret = source.copy_under(destination, name=name).serialize(), http_status.HTTP_201_CREATED update_storage_usage(destination.target) return ret @@ -133,13 +133,13 @@ def osfstorage_move_hook(source, destination, name=None, **kwargs): source_target = source.target try: - ret = source.move_under(destination, name=name).serialize(), httplib.OK + ret = source.move_under(destination, name=name).serialize(), http_status.HTTP_200_OK except exceptions.FileNodeCheckedOutError: - raise HTTPError(httplib.METHOD_NOT_ALLOWED, data={ + raise HTTPError(http_status.HTTP_405_METHOD_NOT_ALLOWED, data={ 'message_long': 'Cannot move file as it is checked out.' }) except exceptions.FileNodeIsPrimaryFile: - raise HTTPError(httplib.FORBIDDEN, data={ + raise HTTPError(http_status.HTTP_403_FORBIDDEN, data={ 'message_long': 'Cannot move file as it is the primary file of preprint.' }) @@ -288,7 +288,7 @@ def osfstorage_create_child(file_node, payload, **kwargs): if getattr(file_node.target, 'is_registration', False) and not getattr(file_node.target, 'archiving', False): raise HTTPError( - httplib.BAD_REQUEST, + http_status.HTTP_400_BAD_REQUEST, data={ 'message_short': 'Registered Nodes are immutable', 'message_long': "The operation you're trying to do cannot be applied to registered Nodes, which are immutable", @@ -296,10 +296,10 @@ def osfstorage_create_child(file_node, payload, **kwargs): ) if not (name or user) or '/' in name: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if getattr(file_node.target, 'is_quickfiles', False) and is_folder: - raise HTTPError(httplib.BAD_REQUEST, data={'message_long': 'You may not create a folder for QuickFiles'}) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data={'message_long': 'You may not create a folder for QuickFiles'}) try: # Create a save point so that we can rollback and unlock @@ -313,7 +313,7 @@ def osfstorage_create_child(file_node, payload, **kwargs): created, file_node = False, parent.find_child_by_name(name, kind=int(not is_folder)) if not created and is_folder: - raise HTTPError(httplib.CONFLICT, data={ + raise HTTPError(http_status.HTTP_409_CONFLICT, data={ 'message_long': 'Cannot create folder "{name}" because a file or folder already exists at path "{path}"'.format( name=file_node.name, path=file_node.materialized_path, @@ -321,7 +321,7 @@ def osfstorage_create_child(file_node, payload, **kwargs): }) if file_node.checkout and file_node.checkout._id != user._id: - raise HTTPError(httplib.FORBIDDEN, data={ + raise HTTPError(http_status.HTTP_403_FORBIDDEN, data={ 'message_long': 'File cannot be updated due to checkout status.' }) @@ -335,7 +335,7 @@ def osfstorage_create_child(file_node, payload, **kwargs): } )) except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) current_version = file_node.get_version() new_version = file_node.create_version(user, location, metadata) @@ -354,7 +354,7 @@ def osfstorage_create_child(file_node, payload, **kwargs): 'archive': not archive_exists, # Should waterbutler also archive this file 'data': file_node.serialize(), 'version': version_id, - }, httplib.CREATED if created else httplib.OK + }, http_status.HTTP_201_CREATED if created else http_status.HTTP_200_OK @must_be_signed @@ -366,17 +366,17 @@ def osfstorage_delete(file_node, payload, target, **kwargs): #TODO Auth check? if not auth: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if file_node == OsfStorageFolder.objects.get_root(target=target): - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) try: file_node.delete(user=user) except exceptions.FileNodeCheckedOutError: - raise HTTPError(httplib.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) except exceptions.FileNodeIsPrimaryFile: - raise HTTPError(httplib.FORBIDDEN, data={ + raise HTTPError(http_status.HTTP_403_FORBIDDEN, data={ 'message_long': 'Cannot delete file as it is the primary file of preprint.' }) @@ -401,7 +401,7 @@ def osfstorage_download(file_node, payload, **kwargs): try: version_id = int(request.args['version']) except ValueError: - raise make_error(httplib.BAD_REQUEST, message_short='Version must be an integer if not specified') + raise make_error(http_status.HTTP_400_BAD_REQUEST, message_short='Version must be an integer if not specified') version = file_node.get_version(version_id, required=True) file_version_thru = version.get_basefilenode_version(file_node) @@ -423,8 +423,8 @@ def osfstorage_download(file_node, payload, **kwargs): def osfstorage_add_tag(file_node, **kwargs): data = request.get_json() if file_node.add_tag(data['tag'], kwargs['auth']): - return {'status': 'success'}, httplib.OK - return {'status': 'failure'}, httplib.BAD_REQUEST + return {'status': 'success'}, http_status.HTTP_200_OK + return {'status': 'failure'}, http_status.HTTP_400_BAD_REQUEST @must_have_permission(WRITE) @decorators.autoload_filenode(must_be='file') @@ -433,11 +433,11 @@ def osfstorage_remove_tag(file_node, **kwargs): try: file_node.remove_tag(data['tag'], kwargs['auth']) except TagNotFoundError: - return {'status': 'failure'}, httplib.CONFLICT + return {'status': 'failure'}, http_status.HTTP_409_CONFLICT except InvalidTagError: - return {'status': 'failure'}, httplib.BAD_REQUEST + return {'status': 'failure'}, http_status.HTTP_400_BAD_REQUEST else: - return {'status': 'success'}, httplib.OK + return {'status': 'success'}, http_status.HTTP_200_OK @must_be_logged_in @@ -449,7 +449,7 @@ def update_region(auth, **kwargs): try: region_id = data['region_id'] except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) try: user_settings.set_region(region_id) diff --git a/addons/owncloud/tests/test_views.py b/addons/owncloud/tests/test_views.py index 4473c433653..ab0f42f71c9 100644 --- a/addons/owncloud/tests/test_views.py +++ b/addons/owncloud/tests/test_views.py @@ -3,7 +3,7 @@ import mock import pytest -import httplib as http +from rest_framework import status as http_status from addons.base.tests.views import ( OAuthAddonAuthViewsTestCaseMixin, OAuthAddonConfigViewsTestCaseMixin @@ -57,7 +57,7 @@ def test_get_config(self): url = self.project.api_url_for( '{0}_get_config'.format(self.ADDON_SHORT_NAME)) res = self.app.get(url, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) assert_in('result', res.json) serialized = self.Serializer().serialize_settings( self.node_settings, diff --git a/addons/owncloud/views.py b/addons/owncloud/views.py index 3456380d285..6399c5bf8b0 100644 --- a/addons/owncloud/views.py +++ b/addons/owncloud/views.py @@ -1,6 +1,6 @@ """Views for the node settings page.""" # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from django.core.exceptions import ValidationError from furl import furl @@ -62,11 +62,11 @@ def owncloud_add_user_account(auth, **kwargs): except requests.exceptions.ConnectionError: return { 'message': 'Invalid ownCloud server.' - }, http.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST except owncloud.owncloud.HTTPResponseError: return { 'message': 'ownCloud Login failed.' - }, http.UNAUTHORIZED + }, http_status.HTTP_401_UNAUTHORIZED provider = OwnCloudProvider(account=None, host=host.url, username=username, password=password) diff --git a/addons/s3/tests/test_view.py b/addons/s3/tests/test_view.py index 7619155861c..3057a0fa3aa 100644 --- a/addons/s3/tests/test_view.py +++ b/addons/s3/tests/test_view.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from boto.exception import S3ResponseError import mock @@ -45,7 +45,7 @@ def test_s3_settings_input_empty_keys(self): 'access_key': '', 'secret_key': '' }, auth=self.user.auth, expect_errors=True) - assert_equals(rv.status_int, http.BAD_REQUEST) + assert_equals(rv.status_int, http_status.HTTP_400_BAD_REQUEST) assert_in('All the fields above are required.', rv.body) def test_s3_settings_input_empty_access_key(self): @@ -54,7 +54,7 @@ def test_s3_settings_input_empty_access_key(self): 'access_key': '', 'secret_key': 'Non-empty-secret-key' }, auth=self.user.auth, expect_errors=True) - assert_equals(rv.status_int, http.BAD_REQUEST) + assert_equals(rv.status_int, http_status.HTTP_400_BAD_REQUEST) assert_in('All the fields above are required.', rv.body) def test_s3_settings_input_empty_secret_key(self): @@ -63,7 +63,7 @@ def test_s3_settings_input_empty_secret_key(self): 'access_key': 'Non-empty-access-key', 'secret_key': '' }, auth=self.user.auth, expect_errors=True) - assert_equals(rv.status_int, http.BAD_REQUEST) + assert_equals(rv.status_int, http_status.HTTP_400_BAD_REQUEST) assert_in('All the fields above are required.', rv.body) def test_s3_set_bucket_no_settings(self): @@ -74,7 +74,7 @@ def test_s3_set_bucket_no_settings(self): url, {'s3_bucket': 'hammertofall'}, auth=user.auth, expect_errors=True ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) def test_s3_set_bucket_no_auth(self): @@ -86,7 +86,7 @@ def test_s3_set_bucket_no_auth(self): url, {'s3_bucket': 'hammertofall'}, auth=user.auth, expect_errors=True ) - assert_equal(res.status_code, http.FORBIDDEN) + assert_equal(res.status_code, http_status.HTTP_403_FORBIDDEN) def test_s3_set_bucket_registered(self): registration = self.project.register_node( @@ -99,7 +99,7 @@ def test_s3_set_bucket_registered(self): expect_errors=True, ) - assert_equal(res.status_code, http.BAD_REQUEST) + assert_equal(res.status_code, http_status.HTTP_400_BAD_REQUEST) @mock.patch('addons.s3.views.utils.can_list', return_value=False) def test_user_settings_cant_list(self, mock_can_list): @@ -108,7 +108,7 @@ def test_user_settings_cant_list(self, mock_can_list): 'access_key': 'aldkjf', 'secret_key': 'las' }, auth=self.user.auth, expect_errors=True) - assert_equals(rv.status_int, http.BAD_REQUEST) + assert_equals(rv.status_int, http_status.HTTP_400_BAD_REQUEST) assert_in('Unable to list buckets.', rv.body) def test_s3_remove_node_settings_owner(self): @@ -159,7 +159,7 @@ def test_set_config(self, mock_location, mock_exists): res = self.app.put_json(url, { 'selected': self.folder }, auth=self.user.auth) - assert_equal(res.status_code, http.OK) + assert_equal(res.status_code, http_status.HTTP_200_OK) self.project.reload() self.node_settings.reload() assert_equal( @@ -266,7 +266,7 @@ def test_create_bucket_pass(self, mock_names, mock_make): auth=self.user.auth ) - assert_equal(ret.status_int, http.OK) + assert_equal(ret.status_int, http_status.HTTP_200_OK) assert_equal(ret.json, {}) @mock.patch('addons.s3.views.utils.create_bucket') diff --git a/addons/s3/utils.py b/addons/s3/utils.py index d84773849b9..0738b5bf397 100644 --- a/addons/s3/utils.py +++ b/addons/s3/utils.py @@ -1,5 +1,5 @@ import re -import httplib +from rest_framework import status as http_status from boto import exception from boto.s3.connection import S3Connection @@ -26,7 +26,7 @@ def get_bucket_names(node_settings): try: buckets = connect_s3(node_settings=node_settings).get_all_buckets() except exception.NoAuthHandlerFound: - raise HTTPError(httplib.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) except exception.BotoServerError as e: raise HTTPError(e.status) diff --git a/addons/s3/views.py b/addons/s3/views.py index b022c4b9dd8..aba5ec8f63c 100644 --- a/addons/s3/views.py +++ b/addons/s3/views.py @@ -1,4 +1,4 @@ -import httplib +from rest_framework import status as http_status from boto import exception from django.core.exceptions import ValidationError @@ -65,12 +65,12 @@ def s3_add_user_account(auth, **kwargs): access_key = request.json['access_key'] secret_key = request.json['secret_key'] except KeyError: - raise HTTPError(httplib.BAD_REQUEST) + raise HTTPError(http_status.HTTP_400_BAD_REQUEST) if not (access_key and secret_key): return { 'message': 'All the fields above are required.' - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST user_info = utils.get_user_info(access_key, secret_key) if not user_info: @@ -78,13 +78,13 @@ def s3_add_user_account(auth, **kwargs): 'message': ('Unable to access account.\n' 'Check to make sure that the above credentials are valid, ' 'and that they have permission to list buckets.') - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST if not utils.can_list(access_key, secret_key): return { 'message': ('Unable to list buckets.\n' 'Listing buckets is required permission that can be changed via IAM') - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST account = None try: @@ -130,14 +130,14 @@ def create_bucket(auth, node_addon, **kwargs): return { 'message': 'That bucket name is not valid.', 'title': 'Invalid bucket name', - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST # Get location and verify it is valid if not utils.validate_bucket_location(bucket_location): return { 'message': 'That bucket location is not valid.', 'title': 'Invalid bucket location', - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST try: utils.create_bucket(node_addon, bucket_name, bucket_location) @@ -145,16 +145,16 @@ def create_bucket(auth, node_addon, **kwargs): return { 'message': e.message, 'title': 'Problem connecting to S3', - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST except exception.S3CreateError as e: return { 'message': e.message, 'title': "Problem creating bucket '{0}'".format(bucket_name), - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST except exception.BotoClientError as e: # Base class catchall return { 'message': e.message, 'title': 'Error connecting to S3', - }, httplib.BAD_REQUEST + }, http_status.HTTP_400_BAD_REQUEST return {} diff --git a/addons/twofactor/tests/test_models.py b/addons/twofactor/tests/test_models.py index f581a4f2261..e501b5fdc57 100644 --- a/addons/twofactor/tests/test_models.py +++ b/addons/twofactor/tests/test_models.py @@ -1,5 +1,5 @@ import unittest -from urlparse import parse_qs, urlparse +from future.moves.urllib.parse import urlparse, parse_qs import pytest from addons.twofactor.tests.utils import _valid_code diff --git a/addons/twofactor/views.py b/addons/twofactor/views.py index 0c2c95837ad..d50fda014dc 100644 --- a/addons/twofactor/views.py +++ b/addons/twofactor/views.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from flask import request @@ -16,13 +16,13 @@ def twofactor_settings_put(user_addon, *args, **kwargs): code = request.json.get('code') if code is None: - raise HTTPError(code=http.BAD_REQUEST) + raise HTTPError(code=http_status.HTTP_400_BAD_REQUEST) if user_addon.verify_code(code): user_addon.is_confirmed = True user_addon.save() - return {'message': 'Successfully verified two-factor authentication.'}, http.OK - raise HTTPError(http.FORBIDDEN, data=dict( + return {'message': 'Successfully verified two-factor authentication.'}, http_status.HTTP_200_OK + raise HTTPError(http_status.HTTP_403_FORBIDDEN, data=dict( message_short='Forbidden', message_long='The two-factor verification code you provided is invalid.' )) @@ -39,7 +39,7 @@ def twofactor_enable(auth, *args, **kwargs): user = auth.user if user.has_addon('twofactor'): - return HTTPError(http.BAD_REQUEST, data=dict(message_long='This user already has two-factor enabled')) + return HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict(message_long='This user already has two-factor enabled')) user.add_addon('twofactor', auth=auth) user_addon = user.get_addon('twofactor') @@ -57,6 +57,6 @@ def twofactor_disable(auth, *args, **kwargs): auth.user.save() return {} else: - raise HTTPError(http.INTERNAL_SERVER_ERROR, data=dict( + raise HTTPError(http_status.HTTP_500_INTERNAL_SERVER_ERROR, data=dict( message_long='Could not disable two-factor at this time' )) diff --git a/addons/wiki/tests/test_wiki.py b/addons/wiki/tests/test_wiki.py index a60b5aaa03d..de976becc25 100644 --- a/addons/wiki/tests/test_wiki.py +++ b/addons/wiki/tests/test_wiki.py @@ -3,7 +3,7 @@ # PEP8 asserts from copy import deepcopy -import httplib as http +from rest_framework import status as http_status import time import mock import pytest @@ -601,7 +601,7 @@ def test_rename_wiki_page_invalid(self, new_name=u'invalid/name'): auth=self.auth, expect_errors=True, ) - assert_equal(http.BAD_REQUEST, res.status_code) + assert_equal(http_status.HTTP_400_BAD_REQUEST, res.status_code) assert_equal(res.json['message_short'], 'Invalid name') assert_equal(res.json['message_long'], 'Page name cannot contain forward slashes.') self.project.reload() diff --git a/addons/wiki/utils.py b/addons/wiki/utils.py index 21f45f554f5..d4a02a45236 100644 --- a/addons/wiki/utils.py +++ b/addons/wiki/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import os -import urllib +from future.moves.urllib.parse import quote import uuid import ssl @@ -131,7 +131,7 @@ def broadcast_to_sharejs(action, sharejs_uuid, node=None, wiki_name='home', data ) if action == 'redirect' or action == 'delete': - redirect_url = urllib.quote( + redirect_url = quote( node.web_url_for('project_wiki_view', wname=wiki_name, _guid=True), safe='', ) diff --git a/addons/wiki/views.py b/addons/wiki/views.py index d40e611a42b..f4bcd90ea02 100644 --- a/addons/wiki/views.py +++ b/addons/wiki/views.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status import logging from flask import request @@ -44,27 +44,27 @@ logger = logging.getLogger(__name__) -WIKI_NAME_EMPTY_ERROR = HTTPError(http.BAD_REQUEST, data=dict( +WIKI_NAME_EMPTY_ERROR = HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid request', message_long='The wiki page name cannot be empty.' )) -WIKI_NAME_MAXIMUM_LENGTH_ERROR = HTTPError(http.BAD_REQUEST, data=dict( +WIKI_NAME_MAXIMUM_LENGTH_ERROR = HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid request', message_long='The wiki page name cannot be more than 100 characters.' )) -WIKI_PAGE_CANNOT_RENAME_ERROR = HTTPError(http.BAD_REQUEST, data=dict( +WIKI_PAGE_CANNOT_RENAME_ERROR = HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid request', message_long='The wiki page cannot be renamed.' )) -WIKI_PAGE_CONFLICT_ERROR = HTTPError(http.CONFLICT, data=dict( +WIKI_PAGE_CONFLICT_ERROR = HTTPError(http_status.HTTP_409_CONFLICT, data=dict( message_short='Page conflict', message_long='A wiki page with that name already exists.' )) -WIKI_PAGE_NOT_FOUND_ERROR = HTTPError(http.NOT_FOUND, data=dict( +WIKI_PAGE_NOT_FOUND_ERROR = HTTPError(http_status.HTTP_404_NOT_FOUND, data=dict( message_short='Not found', message_long='A wiki page could not be found.' )) -WIKI_INVALID_VERSION_ERROR = HTTPError(http.BAD_REQUEST, data=dict( +WIKI_INVALID_VERSION_ERROR = HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid request', message_long='The requested version of this wiki page does not exist.' )) @@ -163,7 +163,7 @@ def project_wiki_delete(auth, wname, **kwargs): sharejs_uuid = wiki_utils.get_sharejs_uuid(node, wiki_name) if not wiki_page: - raise HTTPError(http.NOT_FOUND) + raise HTTPError(http_status.HTTP_404_NOT_FOUND) wiki_page.delete(auth) wiki_utils.broadcast_to_sharejs('delete', sharejs_uuid, node) @@ -241,10 +241,10 @@ def project_wiki_view(auth, wname, path=None, **kwargs): raise WIKI_PAGE_NOT_FOUND_ERROR if 'edit' in request.args: if wiki_settings.is_publicly_editable: - raise HTTPError(http.UNAUTHORIZED) + raise HTTPError(http_status.HTTP_401_UNAUTHORIZED) if node.can_view(auth): return redirect(node.web_url_for('project_wiki_view', wname=wname, _guid=True)) - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) sharejs_uuid = None # Opens 'edit' panel when home wiki is empty @@ -313,7 +313,7 @@ def project_wiki_edit_post(auth, wname, **kwargs): # Create a wiki WikiPage.objects.create_for_node(node, wiki_name, form_wiki_content, auth) ret = {'status': 'success'} - return ret, http.FOUND, None, redirect_url + return ret, http_status.HTTP_302_FOUND, None, redirect_url @must_be_valid_project # injects node or project @must_have_permission(ADMIN) @@ -324,7 +324,7 @@ def edit_wiki_settings(node, auth, **kwargs): permissions = request.get_json().get('permission', None) if not wiki_settings: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid request', message_long='Cannot change wiki settings without a wiki' )) @@ -334,7 +334,7 @@ def edit_wiki_settings(node, auth, **kwargs): elif permissions == 'private': permissions = False else: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid request', message_long='Permissions flag used is incorrect.' )) @@ -342,7 +342,7 @@ def edit_wiki_settings(node, auth, **kwargs): try: wiki_settings.set_editing(permissions, auth, log=True) except NodeStateError as e: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short="Can't change privacy", message_long=str(e) )) @@ -416,7 +416,7 @@ def project_wiki_rename(auth, wname, **kwargs): except NameEmptyError: raise WIKI_NAME_EMPTY_ERROR except NameInvalidError as error: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid name', message_long=error.args[0] )) @@ -429,7 +429,7 @@ def project_wiki_rename(auth, wname, **kwargs): except PageNotFoundError: raise WIKI_PAGE_NOT_FOUND_ERROR except ValidationError as err: - raise HTTPError(http.BAD_REQUEST, data=dict( + raise HTTPError(http_status.HTTP_400_BAD_REQUEST, data=dict( message_short='Invalid request', message_long=err.messages[0] )) @@ -447,7 +447,7 @@ def project_wiki_validate_name(wname, auth, node, **kwargs): wiki = WikiPage.objects.get_for_node(node, wiki_name) if wiki or wiki_name.lower() == 'home': - raise HTTPError(http.CONFLICT, data=dict( + raise HTTPError(http_status.HTTP_409_CONFLICT, data=dict( message_short='Wiki page name conflict.', message_long='A wiki page with that name already exists.' )) diff --git a/addons/zotero/provider.py b/addons/zotero/provider.py index 836688fa29d..6b1cc954d70 100644 --- a/addons/zotero/provider.py +++ b/addons/zotero/provider.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import httplib as http +from rest_framework import status as http_status from framework.exceptions import HTTPError from website.citations.providers import CitationsProvider @@ -102,7 +102,7 @@ def citation_list(self, node_addon, user, list_id, show='all'): while ancestor_id != attached_list_id: if ancestor_id is '__': - raise HTTPError(http.FORBIDDEN) + raise HTTPError(http_status.HTTP_403_FORBIDDEN) ancestor_id = folders[ancestor_id].get('parent_list_id') contents = [] diff --git a/addons/zotero/tests/test_views.py b/addons/zotero/tests/test_views.py index 6b35b72c213..959f1ab54ac 100644 --- a/addons/zotero/tests/test_views.py +++ b/addons/zotero/tests/test_views.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import mock import pytest -import urlparse +from future.moves.urllib.parse import urljoin import responses from framework.auth import Auth @@ -42,9 +42,9 @@ class TestConfigViews(ZoteroTestCase, views.OAuthCitationAddonConfigViewsTestCas def setUp(self): super(TestConfigViews, self).setUp() - self.foldersApiUrl = urlparse.urljoin(API_URL, 'users/{}/collections' + self.foldersApiUrl = urljoin(API_URL, 'users/{}/collections' .format(self.external_account.provider_id)) - self.documentsApiUrl = urlparse.urljoin(API_URL, 'users/{}/items/top' + self.documentsApiUrl = urljoin(API_URL, 'users/{}/items/top' .format(self.external_account.provider_id)) # Sets library key diff --git a/admin/base/settings/defaults.py b/admin/base/settings/defaults.py index 8f84988509c..ea7f7432f87 100644 --- a/admin/base/settings/defaults.py +++ b/admin/base/settings/defaults.py @@ -2,9 +2,6 @@ Django settings for the admin project. """ -import os -from urlparse import urlparse -from website import settings as osf_settings from django.contrib import messages from api.base.settings import * # noqa # TODO ALL SETTINGS FROM API WILL BE IMPORTED AND WILL NEED TO BE OVERRRIDEN @@ -249,7 +246,7 @@ INSTALLED_APPS += ('debug_toolbar', 'nplusone.ext.django',) MIDDLEWARE += ('debug_toolbar.middleware.DebugToolbarMiddleware', 'nplusone.ext.django.NPlusOneMiddleware',) DEBUG_TOOLBAR_CONFIG = { - 'SHOW_TOOLBAR_CALLBACK': lambda(_): True, + 'SHOW_TOOLBAR_CALLBACK': lambda _: True, 'DISABLE_PANELS': { 'debug_toolbar.panels.templates.TemplatesPanel', 'debug_toolbar.panels.redirects.RedirectsPanel' diff --git a/admin/base/urls.py b/admin/base/urls.py index d741d850eac..e39e1db38c0 100644 --- a/admin/base/urls.py +++ b/admin/base/urls.py @@ -1,9 +1,9 @@ from django.conf.urls import include, url from django.contrib import admin -import settings -from . import views +from admin.base.settings import ADMIN_BASE, DEBUG +from admin.base import views -base_pattern = '^{}'.format(settings.ADMIN_BASE) +base_pattern = '^{}'.format(ADMIN_BASE) urlpatterns = [ ### ADMIN ### @@ -38,7 +38,7 @@ ), ] -if settings.DEBUG: +if DEBUG: import debug_toolbar urlpatterns += [ diff --git a/admin/preprints/views.py b/admin/preprints/views.py index 303645d76df..98a9b3a455a 100644 --- a/admin/preprints/views.py +++ b/admin/preprints/views.py @@ -7,6 +7,7 @@ from django.shortcuts import redirect from django.views.defaults import page_not_found from django.core.exceptions import PermissionDenied +from django.contrib import messages from osf.models import SpamStatus, PreprintRequest from osf.models.preprint import Preprint, PreprintLog, OSFUser @@ -68,9 +69,14 @@ def get_success_url(self): return reverse_lazy('preprints:preprint', kwargs={'guid': self.kwargs.get('guid')}) def post(self, request, *args, **kwargs): + old_provider = self.get_object().provider if not request.user.has_perm('osf.change_preprint'): raise PermissionsError("This user does not have permission to update this preprint's provider.") - return super(PreprintView, self).post(request, *args, **kwargs) + response = super(PreprintView, self).post(request, *args, **kwargs) + new_provider = self.get_object().provider + if new_provider and old_provider.id != new_provider.id: + self.update_subjects_for_provider(request, old_provider, new_provider) + return response def get_context_data(self, **kwargs): preprint = Preprint.load(self.kwargs.get('guid')) @@ -81,6 +87,13 @@ def get_context_data(self, **kwargs): kwargs.update({'message': kwargs.get('message')}) return super(PreprintView, self).get_context_data(**kwargs) + def update_subjects_for_provider(self, request, old_provider, new_provider): + subject_problems = self.object.map_subjects_between_providers(old_provider, new_provider, auth=None) + if subject_problems: + messages.warning(request, 'Unable to find subjects in new provider for the following subject(s):') + for problem in subject_problems: + messages.warning(request, problem) + class PreprintSpamList(PermissionRequiredMixin, ListView): SPAM_STATE = SpamStatus.UNKNOWN diff --git a/admin/templates/preprints/preprint.html b/admin/templates/preprints/preprint.html index 578b690ec23..6181a0347d0 100644 --- a/admin/templates/preprints/preprint.html +++ b/admin/templates/preprints/preprint.html @@ -130,6 +130,13 @@