mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge pull request #2937 from carlfeberhard/jsonp
Allow JSONP to be returned from API endpoints
This commit is contained in:
@@ -15,6 +15,8 @@ from galaxy.web.framework import url_for
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
JSON_CONTENT_TYPE = "application/json"
|
||||
JSONP_CONTENT_TYPE = "application/javascript"
|
||||
JSONP_CALLBACK_KEY = 'callback'
|
||||
|
||||
|
||||
def error( message ):
|
||||
@@ -36,15 +38,22 @@ def expose( func ):
|
||||
return func
|
||||
|
||||
|
||||
def json( func, **json_kwargs ):
|
||||
def json( func, pretty=False ):
|
||||
"""
|
||||
Format the response as JSON and set the response content type to
|
||||
JSON_CONTENT_TYPE.
|
||||
"""
|
||||
@wraps(func)
|
||||
def call_and_format( self, trans, *args, **kwargs ):
|
||||
trans.response.set_content_type( JSON_CONTENT_TYPE )
|
||||
return safe_dumps( func( self, trans, *args, **kwargs ), **json_kwargs )
|
||||
# pull out any callback argument to the api endpoint and set the content type to json or javascript
|
||||
jsonp_callback = kwargs.pop( JSONP_CALLBACK_KEY, None )
|
||||
if jsonp_callback:
|
||||
trans.response.set_content_type( JSONP_CONTENT_TYPE )
|
||||
else:
|
||||
trans.response.set_content_type( JSON_CONTENT_TYPE )
|
||||
rval = func( self, trans, *args, **kwargs )
|
||||
return _format_return_as_json( rval, jsonp_callback, pretty=( pretty or trans.debug ) )
|
||||
|
||||
if not hasattr( func, '_orig' ):
|
||||
call_and_format._orig = func
|
||||
return expose( _save_orig_fn( call_and_format, func ) )
|
||||
@@ -54,7 +63,7 @@ def json_pretty( func ):
|
||||
"""
|
||||
Indent and sort returned JSON.
|
||||
"""
|
||||
return json( func, indent=4, sort_keys=True )
|
||||
return json( func, pretty=True )
|
||||
|
||||
|
||||
def require_login( verb="perform this action", use_panels=False, webapp='galaxy' ):
|
||||
@@ -113,9 +122,17 @@ def expose_api( func, to_json=True, user_required=True ):
|
||||
error_status = '400 Bad Request'
|
||||
error_message = 'Your request did not appear to be valid JSON, please consult the API documentation'
|
||||
return error
|
||||
trans.response.set_content_type( "application/json" )
|
||||
|
||||
# pull out any callback argument to the api endpoint and set the content type to json or javascript
|
||||
jsonp_callback = kwargs.pop( JSONP_CALLBACK_KEY, None )
|
||||
if jsonp_callback:
|
||||
trans.response.set_content_type( JSONP_CONTENT_TYPE )
|
||||
else:
|
||||
trans.response.set_content_type( JSON_CONTENT_TYPE )
|
||||
|
||||
# send 'do not cache' headers to handle IE's caching of ajax get responses
|
||||
trans.response.headers[ 'Cache-Control' ] = "max-age=0,no-cache,no-store"
|
||||
|
||||
# Perform api_run_as processing, possibly changing identity
|
||||
if 'payload' in kwargs and 'run_as' in kwargs['payload']:
|
||||
if not trans.user_can_do_run_as():
|
||||
@@ -135,10 +152,8 @@ def expose_api( func, to_json=True, user_required=True ):
|
||||
return "That user does not exist."
|
||||
try:
|
||||
rval = func( self, trans, *args, **kwargs)
|
||||
if to_json and trans.debug:
|
||||
rval = safe_dumps( rval, indent=4, sort_keys=True )
|
||||
elif to_json:
|
||||
rval = safe_dumps( rval )
|
||||
if to_json:
|
||||
rval = _format_return_as_json( rval, jsonp_callback, pretty=trans.debug )
|
||||
return rval
|
||||
except paste.httpexceptions.HTTPException:
|
||||
raise # handled
|
||||
@@ -206,7 +221,7 @@ def expose_api_anonymous( func, to_json=True ):
|
||||
|
||||
# ----------------------------------------------------------------------------- (new) api decorators
|
||||
# TODO: rename as expose_api and make default.
|
||||
def _future_expose_api( func, to_json=True, user_required=True, user_or_session_required=True ):
|
||||
def _future_expose_api( func, to_json=True, user_required=True, user_or_session_required=True, handle_jsonp=True ):
|
||||
"""
|
||||
Expose this function via the API.
|
||||
"""
|
||||
@@ -233,7 +248,13 @@ def _future_expose_api( func, to_json=True, user_required=True, user_or_session_
|
||||
error_code = error_codes.USER_INVALID_JSON
|
||||
return __api_error_response( trans, status_code=400, err_code=error_code )
|
||||
|
||||
trans.response.set_content_type( JSON_CONTENT_TYPE )
|
||||
# pull out any callback argument to the api endpoint and set the content type to json or javascript
|
||||
# TODO: use handle_jsonp to NOT overwrite existing tool_shed JSONP
|
||||
jsonp_callback = kwargs.pop( JSONP_CALLBACK_KEY, None ) if handle_jsonp else None
|
||||
if jsonp_callback:
|
||||
trans.response.set_content_type( JSONP_CONTENT_TYPE )
|
||||
else:
|
||||
trans.response.set_content_type( JSON_CONTENT_TYPE )
|
||||
|
||||
# send 'do not cache' headers to handle IE's caching of ajax get responses
|
||||
trans.response.headers[ 'Cache-Control' ] = "max-age=0,no-cache,no-store"
|
||||
@@ -258,11 +279,9 @@ def _future_expose_api( func, to_json=True, user_required=True, user_or_session_
|
||||
error_code = error_codes.USER_INVALID_RUN_AS
|
||||
return __api_error_response( trans, err_code=error_code, status_code=400 )
|
||||
try:
|
||||
rval = func( self, trans, *args, **kwargs)
|
||||
if to_json and trans.debug:
|
||||
rval = safe_dumps( rval, indent=4, sort_keys=True )
|
||||
elif to_json:
|
||||
rval = safe_dumps( rval )
|
||||
rval = func( self, trans, *args, **kwargs )
|
||||
if to_json:
|
||||
rval = _format_return_as_json( rval, jsonp_callback, pretty=trans.debug )
|
||||
return rval
|
||||
except MessageException as e:
|
||||
traceback_string = format_exc()
|
||||
@@ -288,6 +307,19 @@ def _future_expose_api( func, to_json=True, user_required=True, user_or_session_
|
||||
return decorator
|
||||
|
||||
|
||||
def _format_return_as_json( rval, jsonp_callback=None, pretty=False ):
|
||||
"""
|
||||
Formats a return value as JSON or JSONP if `jsonp_callback` is present.
|
||||
|
||||
Use `pretty=True` to return pretty printed json.
|
||||
"""
|
||||
dumps_kwargs = dict( indent=4, sort_keys=True ) if pretty else {}
|
||||
json = safe_dumps( rval, **dumps_kwargs )
|
||||
if jsonp_callback:
|
||||
json = "{}({});".format( jsonp_callback, json )
|
||||
return json
|
||||
|
||||
|
||||
def __api_error_message( trans, **kwds ):
|
||||
exception = kwds.get( "exception", None )
|
||||
if exception:
|
||||
@@ -362,4 +394,11 @@ def _future_expose_api_raw_anonymous( func ):
|
||||
|
||||
|
||||
def _future_expose_api_raw_anonymous_and_sessionless( func ):
|
||||
return _future_expose_api( func, to_json=False, user_required=False, user_or_session_required=False )
|
||||
# TODO: tool_shed api implemented JSONP first on a method-by-method basis, don't overwrite that for now
|
||||
return _future_expose_api(
|
||||
func,
|
||||
to_json=False,
|
||||
user_required=False,
|
||||
user_or_session_required=False,
|
||||
handle_jsonp=False
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user