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

HARMONY-1721: Switch from using GET to a POST in all OGC coverages calls to harmony #81

Merged
merged 5 commits into from
Mar 14, 2024
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
42 changes: 26 additions & 16 deletions harmony/harmony.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ def __repr__(self) -> str:


class BaseRequest:
"""A Harmony base request with the CMR collection. It is the base class of all harmony requests.
"""A Harmony base request with the CMR collection. It is the base class of all harmony
requests.

Args:
collection: The CMR collection that should be queried
Expand Down Expand Up @@ -522,6 +523,11 @@ def _session(self):
self.session = create_session(self.config, auth=self.auth)
return self.session

def _http_method(self, request: BaseRequest) -> str:
"""Returns the HTTP method to use for the given request."""
method = 'GET' if isinstance(request, CapabilitiesRequest) else 'POST'
return method

def _submit_url(self, request: BaseRequest) -> str:
"""Constructs the URL for the request that is used to submit a new Harmony Job."""
if isinstance(request, CapabilitiesRequest):
Expand Down Expand Up @@ -649,7 +655,7 @@ def _dimension_subset_params(self, request: BaseRequest) -> list:
for dim in request.dimensions:
dim_min = dim.min if dim.min is not None else '*'
dim_max = dim.max if dim.max is not None else '*'
dim_query_param = [f'{dim.name}({dim_min}:{dim_max})']
dim_query_param = f'{dim.name}({dim_min}:{dim_max})'
dimensions.append(dim_query_param)
return dimensions
else:
Expand Down Expand Up @@ -679,25 +685,24 @@ def _files(self, request) -> ContextManager[Mapping[str, Any]]:

def _params_dict_to_files(self, params: Mapping[str, Any]) -> List[Tuple[None, str, None]]:
"""Returns the given parameter mapping as a list of tuples suitable for multipart POST

This method is a temporary need until HARMONY-290 is implemented, since we cannot
currently pass query parameters to a shapefile POST request. Because we need to pass
them as a multipart POST body, we need to inflate them into a list of tuples, one for
each parameter value to allow us to call requests.post(..., files=params)

Args:
params: A dictionary of parameter mappings as returned by self._params(request)

Returns:
A list of tuples suitable for passing to a requests multipart upload corresponding
to the provided parameters
"""
# TODO: remove / refactor after HARMONY-290 is complete
result = []
for key, values in params.items():
if not isinstance(values, list):
values = [values]
result += [(key, (None, str(value), None)) for value in values]
if key == 'granuleId' and isinstance(values, list):
# Include all granuleId values in a single file boundary as a comma separated
# list to avoid creating too many file boundaries
concatenated_values = ','.join(map(str, values))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stupidest join syntax ever

result.append((key, (None, concatenated_values, None)))
else:
if not isinstance(values, list):
values = [values]
result += [(key, (None, str(value), None)) for value in values]
return result

def _get_prepared_request(self, request: BaseRequest) -> requests.models.PreparedRequest:
Expand All @@ -714,28 +719,33 @@ def _get_prepared_request(self, request: BaseRequest) -> requests.models.Prepare
params = self._params(request)
headers = self._headers()

method = self._http_method(request)
with self._files(request) as files:
if files:
if files or method == 'POST':
# Ideally this should just be files=files, params=params but Harmony
# cannot accept both files and query params now. (HARMONY-290)
# Inflate params to a list of tuples that can be passed as multipart
# form-data. This must be done this way due to e.g. "subset" having
# multiple values

# Note: harmony only supports multipart/form-data which is why we use
# the workaround with files rather than `data=params` even when there
# is no shapefile to send

param_items = self._params_dict_to_files(params)
file_items = [(k, v) for k, v in files.items()]
all_files = param_items + file_items

r = requests.models.Request('POST',
self._submit_url(request),
files=param_items + file_items,
files=all_files,
headers=headers)
prepped_request = session.prepare_request(r)
else:
r = requests.models.Request('GET',
self._submit_url(request),
params=params,
headers=headers)
prepped_request = session.prepare_request(r)
prepped_request = session.prepare_request(r)

return prepped_request

Expand Down
Loading
Loading