Skip to content

Commit

Permalink
update tests to work with GV mock and refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
John Tordoff committed May 29, 2024
1 parent d358809 commit 3b164ff
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 83 deletions.
59 changes: 37 additions & 22 deletions addons/base/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,34 +64,55 @@ def format_last_known_metadata(auth, node, file, error_type):
class GravyValetAddonAppConfig:

@staticmethod
def get_configured_storage_addons_data(config_id, auth):
return requests.get(
settings.GV_NODE_ADDON_ENDPOINT.format(config_id=config_id),
params={'include': 'external_storage_service'},
# auth=auth TODO: HMAC
).json()
def get_configured_storage_addons_data(config_id, user):
from osf.external.gravy_valet import auth_helpers as gv_auth
url = settings.GV_NODE_ADDON_ENDPOINT.format(config_id=config_id)

auth_headers = gv_auth.make_gravy_valet_hmac_headers(
request_url=url,
request_method='GET',
requesting_user=user,
)

resp = requests.get(url, headers=auth_headers)
resp.raise_for_status()
return resp.json()

@staticmethod
def get_authorized_storage_account(config_id, auth):
return requests.get(
settings.GV_USER_ADDON_ENDPOINT.format(config_id=config_id),
params={'include': 'external_storage_service'},
# auth=auth TODO: HMAC
).json()

def __init__(self, resource, config_id, auth):
def get_authorized_storage_account(config_id, user):
from osf.external.gravy_valet import auth_helpers as gv_auth
url = settings.GV_USER_ADDON_ENDPOINT.format(config_id=config_id)
auth_headers = gv_auth.make_gravy_valet_hmac_headers(
request_url=url,
request_method='GET',
requesting_user=user,
)

resp = requests.get(url, headers=auth_headers)
resp.raise_for_status()
return resp.json()

def cache_config_id_translation(self):
"""
Cache what legacy addon name corresponds to which config ids.
"""

key = cache_settings.LEGACY_ADDON_KEY.format(target_id=self.config_id)
legacy_addon_cache.set(key, self.addon_name, settings.STORAGE_USAGE_CACHE_TIMEOUT)

def __init__(self, resource, config_id, user):
self.resource = resource
self.user = user
self.FOLDER_SELECTED = self.legacy_app_config.FOLDER_SELECTED
self.NODE_AUTHORIZED = self.legacy_app_config.NODE_DEAUTHORIZED
self.NODE_DEAUTHORIZED = self.legacy_app_config.NODE_DEAUTHORIZED
self.actions = self.legacy_app_config.actions

from osf.models import OSFUser, AbstractNode
if isinstance(resource, AbstractNode):
self.gv_data = self.get_configured_storage_addons_data(config_id, auth)
self.gv_data = self.get_configured_storage_addons_data(config_id, user)
elif isinstance(resource, OSFUser):
self.gv_data = self.get_authorized_storage_account(config_id, auth)
self.gv_data = self.get_authorized_storage_account(config_id, user)
else:
raise NotImplementedError()

Expand All @@ -100,12 +121,6 @@ def cache_config_id_translation(self):
self.cache_config_id_translation()
self.legacy_app_config = settings.ADDONS_AVAILABLE_DICT[self.addon_name]

self.resource = resource
self.auth = auth
self.FOLDER_SELECTED = self.legacy_app_config.FOLDER_SELECTED
self.NODE_AUTHORIZED = self.legacy_app_config.NODE_DEAUTHORIZED
self.NODE_DEAUTHORIZED = self.legacy_app_config.NODE_DEAUTHORIZED
self.actions = self.legacy_app_config.actions

@property
def config(self):
Expand Down
25 changes: 14 additions & 11 deletions api/base/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,20 +634,23 @@ def bulk_get_file_nodes_from_wb_resp(self, files_list):
objs_to_create = defaultdict(lambda: [])
file_objs = []

provider_short_name = files_list[0]['provider']

if waffle.flag_is_active(self.request, features.ENABLE_GV):
user = getattr(self.request, 'user', None)
gv_config = GravyValetAddonAppConfig(
resource=self,
config_id=provider_short_name,
auth=user,
)
provider_short_name = gv_config.addon_name


for item in files_list:
attrs = item['attributes']
if waffle.flag_is_active(self.request, features.ENABLE_GV):
gv_config = GravyValetAddonAppConfig(
self,
attrs['provider'],
auth=get_user_auth(self.request),
)
short_name = gv_config.legacy_app_config.short_name
else:
short_name = attrs['provider']

base_class = BaseFileNode.resolve_class(
short_name,
provider_short_name,
BaseFileNode.FOLDER if attrs['kind'] == 'folder'
else BaseFileNode.FILE,
)
Expand Down Expand Up @@ -676,7 +679,7 @@ def bulk_get_file_nodes_from_wb_resp(self, files_list):

# TODO: Improve robustness
if waffle.flag_is_active(self.request, features.ENABLE_GV):
file_obj.provider = gv_config.node_settings.config_id
file_obj.provider = gv_config.legacy_app_config.config_id

file_obj.update(None, attrs, user=self.request.user, save=False)

Expand Down
81 changes: 52 additions & 29 deletions api_tests/providers/test_files_with_gv.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@
from waffle.testutils import override_flag
from django.shortcuts import reverse
from api_tests.draft_nodes.views.test_draft_node_files_lists import prepare_mock_wb_response
import pytest
import requests
from http import HTTPStatus

from osf.external.gravy_valet import (
auth_helpers as gv_auth,
gv_mocks
)
from osf_tests import factories
from website.settings import GRAVYVALET_URL




@pytest.mark.django_db
Expand All @@ -32,24 +44,22 @@ def provider_gv_id(self):
return 1

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

@pytest.fixture()
def addon_files_url(self, node, provider_gv_id):
def addon_files_url(self, resource, provider_gv_id):
return reverse(
'nodes:node-storage-provider-detail',
kwargs={
'version': 'v2',
'node_id': node._id,
'node_id': resource._id,
'provider': provider_gv_id
}
)

@responses.activate
def test_must_have_auth(self, app, node, addon_files_url):
def test_must_have_auth(self, app, resource, addon_files_url):
res = app.get(addon_files_url, expect_errors=True)
assert res.status_code == 401

Expand Down Expand Up @@ -79,6 +89,28 @@ def override_flag(self):
with override_flag(features.ENABLE_GV, active=True):
yield

@pytest.fixture
def mock_gv(self):
mock_gv = gv_mocks.MockGravyValet()
mock_gv.validate_headers = False
return mock_gv

@pytest.fixture
def external_service(self, mock_gv):
return mock_gv.configure_mock_provider('box')

@pytest.fixture
def external_account(self, mock_gv, user, external_service):
return mock_gv.configure_mock_account(user, external_service.name)

@pytest.fixture
def configured_addon(self, mock_gv, resource, external_account):
return mock_gv.configure_mock_addon(resource, external_account)

@pytest.fixture
def account_one(self, user, external_service, mock_gv):
return mock_gv.configure_mock_account(user, external_service.name)

@pytest.fixture()
def user(self):
return AuthUserFactory()
Expand All @@ -88,46 +120,43 @@ def provider_gv_id(self):
return 1

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

@pytest.fixture()
def file(self, user, node):
def file(self, user, resource):
return api_utils.create_test_file(
node,
resource,
user,
path='Test path',
create_guid=False,
provider=1
)

@pytest.fixture()
def file_url(self, node, provider_gv_id, file):
def file_url(self, resource, provider_gv_id, file):
return reverse(
'nodes:node-files',
kwargs={
'version': 'v2',
'node_id': node._id,
'node_id': resource._id,
'provider': f'{provider_gv_id}/',
'path': f'{file._id}/'
}
)

@responses.activate
def test_must_have_auth(self, app, user, file_url, file, provider_gv_id, node):
def test_must_have_auth(self, app, user, file_url, file, provider_gv_id, mock_gv, resource):
prepare_mock_wb_response(
node=node,
node=resource,
path=file.path + '/',
provider='1',
status_code=505 # invalid auth should not reach mock
)

responses.add_passthru('http://192.168.168.167:8004/v1/configured-storage-addons/1/base_account/')
responses.add_passthru('http://192.168.168.167:8004/v1/authorized-storage-accounts/2/external_storage_service/')

res = app.get(
file_url,
auth=('invaid', 'auth'),
Expand All @@ -137,29 +166,25 @@ def test_must_have_auth(self, app, user, file_url, file, provider_gv_id, node):
assert res.status_code == 401

@responses.activate
def test_must_be_contributor(self, app, user, file_url, file, provider_gv_id, node):
def test_must_be_contributor(self, app, user, file_url, file, provider_gv_id, mock_gv, account_one, configured_addon, resource):
prepare_mock_wb_response(
node=node,
node=resource,
path=file.path + '/',
provider='1',
status_code=403
)

responses.add_passthru('http://192.168.168.167:8004/v1/configured-storage-addons/1/base_account/')
responses.add_passthru('http://192.168.168.167:8004/v1/authorized-storage-accounts/2/external_storage_service/')

res = app.get(
file_url,
auth=AuthUserFactory().auth,
expect_errors=True
)
assert res.status_code == 403

@responses.activate
def test_get_file_provider(self, app, user, file_url, file, provider_gv_id, node):
def test_get_file_provider(self, app, user, file_url, file, provider_gv_id, resource, mock_gv, account_one):
prepare_mock_wb_response(
path=file.path + '/',
node=node,
node=resource,
provider='1',
files=[
{
Expand All @@ -176,10 +201,8 @@ def test_get_file_provider(self, app, user, file_url, file, provider_gv_id, node
]
)

# responses.add_passthru('http://192.168.168.167:7777/v1/resources/2839a/providers/1/66461cb004537100a208ffdd/?meta=True&view_only')
responses.add_passthru('http://192.168.168.167:8004/v1/configured-storage-addons/1/base_account/')
responses.add_passthru('http://192.168.168.167:8004/v1/authorized-storage-accounts/2/external_storage_service/')
res = app.get(file_url, auth=user.auth)
with mock_gv.run_mock():
res = app.get(file_url, auth=user.auth)

assert res.status_code == 200
data = res.json['data']
Expand Down
15 changes: 4 additions & 11 deletions osf/models/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,17 +486,10 @@ def addons(self):
return self.get_addons()

def get_addons(self):
request, user_id = get_request_and_user_id()
if waffle.flag_is_active(request, features.ENABLE_GV):
return [_f for _f in [
self.get_addon(config.short_name)
for config in self.ADDONS_AVAILABLE
] if _f]
else:
return [_f for _f in [
super(self.__class__, self).get_addon(config.short_name)
for config in self.ADDONS_AVAILABLE
] if _f]
return [_f for _f in [
self.get_addon(config.short_name)
for config in self.ADDONS_AVAILABLE
] if _f]

def get_oauth_addons(self):
# TODO: Using hasattr is a dirty hack - we should be using issubclass().
Expand Down
5 changes: 3 additions & 2 deletions osf/models/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -2436,8 +2436,9 @@ def _remove_from_associated_collections(self, auth=None, force=False):
def get_addon(self, name, is_deleted=False):
request, user_id = get_request_and_user_id()

if waffle.flag_is_active(request, features.ENABLE_GV):
return GravyValetAddonAppConfig(self, name, auth=get_user_auth(request))
if waffle.flag_is_active(request, features.ENABLE_GV) and name not in ('osfstorage', 'wiki'):
user = getattr(request, 'user', None)
return GravyValetAddonAppConfig(self, name, user)
else:
return super().get_addon(name, is_deleted)

Expand Down
5 changes: 3 additions & 2 deletions osf/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -1229,8 +1229,9 @@ def create_unregistered(cls, fullname, email=None):
def get_addon(self, name, is_deleted=False):
request, user_id = get_request_and_user_id()

if waffle.flag_is_active(request, features.ENABLE_GV):
return GravyValetAddonAppConfig(self, name, auth=get_user_auth(request))
if waffle.flag_is_active(request, features.ENABLE_GV) and name not in ('osfstorage', 'wiki'):
user = getattr(request, 'user', None)
return GravyValetAddonAppConfig(self, name, user)
else:
return super().get_addon(name, is_deleted)

Expand Down
12 changes: 6 additions & 6 deletions website/settings/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -2142,12 +2142,12 @@ def from_node_usage(cls, usage_bytes, private_limit=None, public_limit=None):

WAFFLE_VALUES_YAML = 'osf/features.yaml'
DEFAULT_DRAFT_NODE_TITLE = 'Untitled'
GV_RESOURCE_DOMAIN = 'http://192.168.168.167:8004/v1/resource-references/?filter[resource_uri]={owner_uri}'
GV_USER_DOMAIN = 'http://192.168.168.167:8004/v1/user-references/?filter[user_uri]={owner_uri}'
GV_API_ROOT = 'http://192.168.168.167:8004/v1/'
GV_RESOURCE_DOMAIN = GRAVYVALET_URL + '/v1/resource-references/?filter[resource_uri]={owner_uri}'
GV_USER_DOMAIN = GRAVYVALET_URL + '/v1/user-references/?filter[user_uri]={owner_uri}'
GV_API_ROOT = GRAVYVALET_URL + '/v1/'
GV_RESOURCE_ENDPOINT = GV_API_ROOT + 'resource-references/?filter[resource_uri]={resource_uri}'
GV_USER_ENDPOINT = GV_API_ROOT + 'user-references/?filter[user_uri]={owner_uri}'
# These two are for `get_addon` vs `get_addons`
GV_USER_ADDON_ENDPOINT = 'http://192.168.168.167:8004/v1/authorized-storage-accounts/{config_id}'
GV_NODE_ADDON_ENDPOINT = 'http://192.168.168.167:8004/v1/configured-storage-addons/{config_id}/'
GV_EXTERNAL_STORAGE_ENDPOINT = 'http://192.168.168.167:8004/v1/external-storage-services/{service_id}'
GV_USER_ADDON_ENDPOINT = GRAVYVALET_URL + '/v1/authorized-storage-accounts/{config_id}'
GV_NODE_ADDON_ENDPOINT = GRAVYVALET_URL + '/v1/configured-storage-addons/{config_id}/'
GV_EXTERNAL_STORAGE_ENDPOINT = GRAVYVALET_URL + '/v1/external-storage-services/{service_id}'

0 comments on commit 3b164ff

Please sign in to comment.