Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENG-4961] Allow Bitbucket full configuration via V2 API #10489

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion addons/bitbucket/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
from framework.auth import Auth
from osf.models.external import ExternalProvider
from osf.models.files import File, Folder, BaseFileNode
from rest_framework import exceptions as drf_exceptions
from website import settings
from website.util import web_url_for
from website.util import web_url_for, api_v2_url

hook_domain = bitbucket_settings.HOOK_DOMAIN or settings.DOMAIN

Expand Down Expand Up @@ -454,3 +455,46 @@ def before_make_public(self, node):

def after_delete(self, user):
self.deauthorize(Auth(user=user), log=True)

def get_folders(self, path, folder_id):
return [{
'id': repo['full_name'],
'path': '/',
'addon': 'bitbucket',
'kind': 'folder',
'name': repo['full_name'],
'urls': {
'folders': api_v2_url(f'nodes/{self.owner._id}/addons/bitbucket/folders/'),
}
} for repo in BitbucketClient(access_token=self.external_account.oauth_key).repos()]

def set_folder(self, folder_id, auth):
connection = BitbucketClient(access_token=self.external_account.oauth_key)

# Validate folder_id is correct format
try:
username, repo = folder_id.split('/')
except ValueError:
raise drf_exceptions.PermissionDenied(f'The credentials provided are incorrect.')

# Validate folder_id is correct value
if not connection.repo(username, repo):
raise drf_exceptions.PermissionDenied(f'The credentials provided are incorrect.')

if folder_id != self.repo:
# Change NodeSettings and log
self.repo = repo
self.user = username
self.owner.add_log(
action='bitbucket_repo_linked',
params={
'project': self.owner.parent_id,
'node': self.owner._id,
'bitbucket': {
'user': self.external_account.display_name,
'repo': folder_id
}
},
auth=auth
)
self.save()
4 changes: 2 additions & 2 deletions api/base/settings/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,8 +302,8 @@
VARNISH_SERVERS = osf_settings.VARNISH_SERVERS
ESI_MEDIA_TYPES = osf_settings.ESI_MEDIA_TYPES

ADDONS_FOLDER_CONFIGURABLE = ['box', 'dropbox', 's3', 'googledrive', 'figshare', 'owncloud', 'onedrive']
ADDONS_OAUTH = ADDONS_FOLDER_CONFIGURABLE + ['dataverse', 'github', 'bitbucket', 'gitlab', 'mendeley', 'zotero', 'forward', 'boa']
ADDONS_FOLDER_CONFIGURABLE = ['box', 'dropbox', 's3', 'googledrive', 'figshare', 'owncloud', 'onedrive', 'bitbucket']
ADDONS_OAUTH = ADDONS_FOLDER_CONFIGURABLE + ['dataverse', 'github', 'gitlab', 'mendeley', 'zotero', 'forward', 'boa']

BYPASS_THROTTLE_TOKEN = 'test-token'

Expand Down
Empty file.
73 changes: 73 additions & 0 deletions api_tests/addons_tests/bitbucket/test_bitbucket_configure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import mock
import pytest
from framework.auth.core import Auth
from api.base.settings.defaults import API_BASE
from osf_tests.factories import ProjectFactory, AuthUserFactory, ExternalAccountFactory
from addons.bitbucket.tests.factories import BitbucketUserSettingsFactory

_mock = lambda attributes: type('MockObject', (mock.Mock,), attributes)


def mock_bitbucket_client():
return _mock({
'repos': lambda *args, **kwargs: [
{
'full_name': 'bitbucket-user-name/test-folder'
}
],
'repo': _mock({})
})


@pytest.mark.django_db
class TestBitbucketConfig:
"""
This class tests features added for our POSE grant which will enable us to access of Bitbucket Addon entirely via
osf.io's v2 REST API. This requires giving the user the ability to configure the base folder for their bitbucket
storage via the API.

"""

@pytest.fixture()
def user(self):
return AuthUserFactory()

@pytest.fixture()
def node(self, user):
return ProjectFactory(creator=user)

@pytest.fixture()
def enabled_addon(self, node, user):
addon = node.get_or_add_addon('bitbucket', auth=Auth(user))
addon.user_settings = BitbucketUserSettingsFactory(owner=user)
addon.save()
return addon

@pytest.fixture()
def node_with_authorized_addon(self, user, node, enabled_addon):
external_account = ExternalAccountFactory(provider='bitbucket')
enabled_addon.external_account = external_account
user_settings = enabled_addon.user_settings
user_settings.oauth_grants[node._id] = {enabled_addon.external_account._id: []}
user_settings.save()
user.external_accounts.add(external_account)
user.save()
enabled_addon.save()
return node

@mock.patch('addons.bitbucket.models.BitbucketClient', return_value=mock_bitbucket_client())
def test_addon_folders_PATCH(self, mock_bitbucket, app, node_with_authorized_addon, user):
resp = app.patch_json_api(
f'/{API_BASE}nodes/{node_with_authorized_addon._id}/addons/bitbucket/',
{
'data': {
'attributes': {
'folder_id': 'bitbucket-user-name/test-folder'
}
}
},
auth=user.auth
)
assert resp.status_code == 200
assert resp.json['data']['attributes']['folder_id'] == 'test-folder'
assert resp.json['data']['attributes']['folder_path'] == 'test-folder'
23 changes: 22 additions & 1 deletion api_tests/nodes/views/test_node_addons.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from api.base.settings.defaults import API_BASE
from osf_tests.factories import AuthUserFactory
from tests.base import ApiAddonTestCase
from api_tests.addons_tests.bitbucket.test_bitbucket_configure import mock_bitbucket_client

from addons.mendeley.tests.factories import (
MendeleyAccountFactory, MendeleyNodeSettingsFactory
Expand Down Expand Up @@ -680,11 +681,15 @@ class TestNodeWikiAddon(NodeUnmanageableAddonTestSuiteMixin, ApiAddonTestCase):

# OAUTH

class TestNodeBitbucketAddon(NodeOAuthAddonTestSuiteMixin, ApiAddonTestCase):
class TestNodeBitbucketAddon(NodeConfigurableAddonTestSuiteMixin, ApiAddonTestCase):
short_name = 'bitbucket'
AccountFactory = BitbucketAccountFactory
NodeSettingsFactory = BitbucketNodeSettingsFactory

@property
def _mock_folder_info(self):
return {'folder_id': 'jasonkelece/0987654321'}

def _settings_kwargs(self, node, user_settings):
return {
'user_settings': self.user_settings,
Expand All @@ -693,6 +698,22 @@ def _settings_kwargs(self, node, user_settings):
'owner': self.node
}

def test_folder_list_GET_expected_behavior(self):
with mock.patch('addons.bitbucket.models.BitbucketClient', return_value=mock_bitbucket_client()):
super(TestNodeBitbucketAddon, self).test_folder_list_GET_expected_behavior()

def test_settings_detail_PUT_all_sets_settings(self):
with mock.patch('addons.bitbucket.models.BitbucketClient', return_value=mock_bitbucket_client()):
super(TestNodeBitbucketAddon, self).test_settings_detail_PUT_all_sets_settings()

@property
def _mock_folder_result(self):
return {
'name': 'bitbucket-user-name/test-folder',
'path': '/',
'id': 'bitbucket-user-name/test-folder',
}


class TestNodeDataverseAddon(NodeOAuthAddonTestSuiteMixin, ApiAddonTestCase):
short_name = 'dataverse'
Expand Down
Loading