1010import uuid
1111import json
1212import time
13+ import re
1314
1415import numpy as np
1516
@@ -210,7 +211,39 @@ def _request(self, method, url, params=None, data=None, headers=None,
210211
211212 return all_responses
212213
213- def _get_col_config (self , service , fetch_name = None ):
214+ def _request_w_cache (self , method , url , data = None , headers = None , retrieve_all = True ,
215+ cache = False , cache_opts = None ):
216+ # Note: the method only exposes 4 parameters of the underlying _request() function
217+ # to play nice with existing mocks
218+ # Caching: follow BaseQuery._request()'s pattern, which uses an AstroQuery object
219+ if not cache :
220+ response = self ._request (method , url , data = data , headers = headers , retrieve_all = retrieve_all )
221+ else :
222+ cacher = self ._get_cacher (method , url , data , headers , retrieve_all )
223+ response = cacher .from_cache (self .cache_location )
224+ if not response :
225+ response = self ._request (method , url , data = data , headers = headers , retrieve_all = retrieve_all )
226+ to_cache (response , cacher .request_file (self .cache_location ))
227+ return response
228+
229+ def _get_cacher (self , method , url , data , headers , retrieve_all ):
230+ """
231+ Return an object that can cache the HTTP request based on the supplied arguments
232+ """
233+
234+ # cacheBreaker parameter (to underlying MAST service) is not relevant (and breaks) local caching
235+ # remove it from part of the cache key
236+ data_no_cache_breaker = re .sub (r'^(.+)cacheBreaker%22%3A%20%22.+%22' , r'\1' , data )
237+ # include retrieve_all as part of the cache key by appending it to data
238+ # it cannot be added as part of req_kwargs dict, as it will be rejected by AstroQuery
239+ data_w_retrieve_all = data_no_cache_breaker + " retrieve_all={}" .format (retrieve_all )
240+ req_kwargs = dict (
241+ data = data_no_cache_breaker ,
242+ headers = headers
243+ )
244+ return AstroQuery (method , url , ** req_kwargs )
245+
246+ def _get_col_config (self , service , fetch_name = None , cache = False ):
214247 """
215248 Gets the columnsConfig entry for given service and stores it in `self._column_configs`.
216249
@@ -246,7 +279,7 @@ def _get_col_config(self, service, fetch_name=None):
246279 if more :
247280 mashup_request = {'service' : all_name , 'params' : {}, 'format' : 'extjs' }
248281 req_string = _prepare_service_request_string (mashup_request )
249- response = self ._request ("POST" , self .MAST_REQUEST_URL , data = req_string , headers = headers )
282+ response = self ._request_w_cache ("POST" , self .MAST_REQUEST_URL , data = req_string , headers = headers , cache = cache )
250283 json_response = response [0 ].json ()
251284
252285 self ._column_configs [service ].update (json_response ['data' ]['Tables' ][0 ]
@@ -300,7 +333,7 @@ def _parse_result(self, responses, verbose=False):
300333 return all_results
301334
302335 @class_or_instance
303- def service_request_async (self , service , params , pagesize = None , page = None , ** kwargs ):
336+ def service_request_async (self , service , params , pagesize = None , page = None , cache = False , cache_opts = None , ** kwargs ):
304337 """
305338 Given a Mashup service and parameters, builds and excecutes a Mashup query.
306339 See documentation `here <https://mast.stsci.edu/api/v0/class_mashup_1_1_mashup_request.html>`__
@@ -320,6 +353,10 @@ def service_request_async(self, service, params, pagesize=None, page=None, **kwa
320353 Default None.
321354 Can be used to override the default behavior of all results being returned to obtain
322355 a specific page of results.
356+ cache : Boolean, optional
357+ try to use cached the query result if set to True
358+ cache_opts : dict, optional
359+ cache options, details TBD, e.g., cache expiration policy, etc.
323360 **kwargs :
324361 See MashupRequest properties
325362 `here <https://mast.stsci.edu/api/v0/class_mashup_1_1_mashup_request.html>`__
@@ -333,7 +370,7 @@ def service_request_async(self, service, params, pagesize=None, page=None, **kwa
333370 # setting self._current_service
334371 if service not in self ._column_configs .keys ():
335372 fetch_name = kwargs .pop ('fetch_name' , None )
336- self ._get_col_config (service , fetch_name )
373+ self ._get_col_config (service , fetch_name , cache )
337374 self ._current_service = service
338375
339376 # setting up pagination
@@ -359,12 +396,12 @@ def service_request_async(self, service, params, pagesize=None, page=None, **kwa
359396 mashup_request [prop ] = value
360397
361398 req_string = _prepare_service_request_string (mashup_request )
362- response = self ._request ("POST" , self .MAST_REQUEST_URL , data = req_string , headers = headers ,
363- retrieve_all = retrieve_all )
399+ response = self ._request_w_cache ("POST" , self .MAST_REQUEST_URL , data = req_string , headers = headers ,
400+ retrieve_all = retrieve_all , cache = cache , cache_opts = cache_opts )
364401
365402 return response
366403
367- def build_filter_set (self , column_config_name , service_name = None , ** filters ):
404+ def build_filter_set (self , column_config_name , service_name = None , cache = False , ** filters ):
368405 """
369406 Takes user input dictionary of filters and returns a filterlist that the Mashup can understand.
370407
@@ -392,7 +429,7 @@ def build_filter_set(self, column_config_name, service_name=None, **filters):
392429 service_name = column_config_name
393430
394431 if not self ._column_configs .get (service_name ):
395- self ._get_col_config (service_name , fetch_name = column_config_name )
432+ self ._get_col_config (service_name , fetch_name = column_config_name , cache = cache )
396433
397434 caom_col_config = self ._column_configs [service_name ]
398435
0 commit comments