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

Dataverse External Tools integration #180

Merged
merged 3 commits into from
Dec 3, 2018
Merged
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
1 change: 1 addition & 0 deletions plugin.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ add_python_test(dataverse
plugins/wholetale/dataverse_lookup.txt
plugins/wholetale/dataverse_listFiles.json
)
add_python_test(integration PLUGIN wholetale)
add_python_style_test(python_static_analysis_wholetale
"${PROJECT_SOURCE_DIR}/plugins/wholetale/server")
44 changes: 44 additions & 0 deletions plugin_tests/integration_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from tests import base


def setUpModule():
base.enabledPlugins.append('wholetale')
base.startServer()


def tearDownModule():
base.stopServer()


class IntegrationTestCase(base.TestCase):

def testDataverseIntegration(self):
resp = self.request(
'/integration/dataverse', method='GET',
params={'fileId': 'blah', 'siteUrl': 'https://dataverse.someplace'})
self.assertStatus(resp, 400)
self.assertEqual(resp.json, {
'message': 'Invalid fileId (should be integer)',
'type': 'rest'
})

resp = self.request(
'/integration/dataverse', method='GET',
params={'fileId': '1234', 'siteUrl': 'definitely not a URL'})
self.assertStatus(resp, 400)
self.assertEqual(resp.json, {
'message': 'Not a valid URL: siteUrl',
'type': 'rest'
})

resp = self.request(
'/integration/dataverse', method='GET',
params={'fileId': '1234', 'siteUrl': 'https://dataverse.someplace'})
self.assertStatus(resp, 303)
self.assertEqual(
resp.headers['Location'],
'https://dashboard.wholetale.org/compose?uri='
'https%3A%2F%2Fdataverse.someplace%2Fapi%2Faccess%2Fdatafile%2F1234'
)
2 changes: 2 additions & 0 deletions server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from .rest.dataset import Dataset
from .rest.recipe import Recipe
from .rest.image import Image
from .rest.integration import Integration
from .rest.repository import Repository
from .rest.publish import Publish
from .rest.harvester import listImportedData
Expand Down Expand Up @@ -374,6 +375,7 @@ def load(info):
events.bind('model.user.save.created', 'wholetale', addDefaultFolders)
info['apiRoot'].repository = Repository()
info['apiRoot'].publish = Publish()
info['apiRoot'].integration = Integration()
info['apiRoot'].folder.route('GET', ('registered',), listImportedData)
info['apiRoot'].folder.route('GET', (':id', 'listing'), listFolder)
info['apiRoot'].folder.route('GET', (':id', 'dataset'), getDataSet)
Expand Down
55 changes: 55 additions & 0 deletions server/lib/dataverse/integration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import cherrypy
import validators
from urllib.parse import urlparse, urlunparse, urlencode
from urllib.error import HTTPError, URLError
from girder.api import access
from girder.api.describe import Description, autoDescribeRoute
from girder.api.rest import RestException, setResponseHeader, boundHandler

from .provider import DataverseImportProvider


@access.public
@autoDescribeRoute(
Description('Convert external tools request and bounce it to the dashboard.')
.param('fileId', 'The Dataverse database ID of a file the external tool has '
'been launched on.', required=True)
.param('siteUrl', 'The URL of the Dataverse installation that hosts the file '
'with the fileId above', required=True)
.param('apiToken', 'The Dataverse API token of the user launching the external'
' tool, if available.', required=False)
.notes('apiToken is currently ignored.')
)
@boundHandler()
def dataverseExternalTools(self, fileId, siteUrl, apiToken):
if not validators.url(siteUrl):
raise RestException('Not a valid URL: siteUrl')
try:
fileId = int(fileId)
except (TypeError, ValueError):
raise RestException('Invalid fileId (should be integer)')

site = urlparse(siteUrl)
url = '{scheme}://{netloc}/api/access/datafile/{fileId}'.format(
scheme=site.scheme, netloc=site.netloc, fileId=fileId
)
query = {'uri': url}
try:
title, _, _ = DataverseImportProvider._parse_access_url(urlparse(url))
query['name'] = title
except (HTTPError, URLError):
# This doesn't bode well, but let's fail later when tale import happens
pass

# TODO: Make base url a plugin setting, defaulting to dashboard.<domain>
dashboard_url = os.environ.get('DASHBOARD_URL', 'https://dashboard.wholetale.org')
location = urlunparse(
urlparse(dashboard_url)._replace(
path='/compose',
query=urlencode(query))
)
setResponseHeader('Location', location)
cherrypy.response.status = 303
13 changes: 13 additions & 0 deletions server/rest/integration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from girder.api.rest import Resource
from ..lib.dataverse.integration import dataverseExternalTools


class Integration(Resource):

def __init__(self):
super(Integration, self).__init__()
self.resourceName = 'integration'

self.route('GET', ('dataverse',), dataverseExternalTools)