Skip to content

Commit

Permalink
Merge pull request #658 from garlic-os/issue-648-missing-gunw-error-msg
Browse files Browse the repository at this point in the history
Add None checks to aws.get_s3_file
  • Loading branch information
jlmaurer authored Jul 9, 2024
2 parents 4c413ad + b23302a commit e37c73c
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 70 deletions.
9 changes: 5 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ and this project adheres to [PEP 440](https://www.python.org/dev/peps/pep-0440/)
and uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Changed
* [657](https://github.com/dbekaert/RAiDER/pull/657) - Fixed a few typos in `README.md`.
### Fixed
* [627](https://github.com/dbekaert/RAiDER/pull/627) - Made Python datetimes timezone-aware and added unit tests and bug fixes.
* [651](https://github.com/dbekaert/RAiDER/pull/651) - Removed use of deprecated argument to `pandas.read_csv`.
* [627](https://github.com/dbekaert/RAiDER/pull/627) - Made Python datetimes timezone-aware and add unit tests and bug fixes.
* [657](https://github.com/dbekaert/RAiDER/pull/657) - Fixed a few typos in `README.md`.
* [658](https://github.com/dbekaert/RAiDER/pull/658) - Fixed opaque error message if a GUNW file is not produced while HyP3 independently against a previous INSAR_ISCE.
* [661](https://github.com/dbekaert/RAiDER/pull/661) - Fixed bug in raiderDownloadGNSS, removed call to scipy.sum, and added unit tests.
* [662](https://github.com/dbekaert/RAiDER/pull/662) - Ensures dem-stitcher to be >= v2.5.6, which updates the url for reading the Geoid EGM 2008.
* [661](https://github.com/dbekaert/RAiDER/pull/661) - Fix bug in raiderDownloadGNSS, remove call to scipy.sum, and add unit tests

## [0.5.1]
### Changed
Expand Down
2 changes: 2 additions & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,5 @@ dependencies:
- jupyter_contrib_nbextensions
- jupyterlab
- wand
# For development
- boto3-stubs
83 changes: 83 additions & 0 deletions test/test_raises_for_missing_gunw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
'''
Regression tests for issue #648:
Bad error message when GUNW file missing in S3 bucket
Program should raise an error if the GUNW product file, metadata file,
or browse image is missing that clearly explains what went wrong, as opposed to
a generic error message resulting from a side effect of the error.
'''
from contextlib import contextmanager
from typing import List

import pytest
from test import TEST_DIR

import shutil
from tempfile import TemporaryDirectory
from pathlib import Path
import RAiDER.aws
import RAiDER.cli.raider

EXAMPLE_GUNW_PATH = Path(TEST_DIR) / 'gunw_test_data/S1-GUNW-D-R-059-tops-20230320_20220418-180300-00179W_00051N-PP-c92e-v2_0_6.nc'
EXAMPLE_JSON_DATA_PATH = Path(TEST_DIR) / 'gunw_test_data/S1-GUNW-A-R-064-tops-20210723_20210711-015001-35393N_33512N-PP-6267-v2_0_4.json'


@pytest.fixture
def iargs() -> List[str]:
return [
'--bucket', 'dummy-bucket',
'--input-bucket-prefix', 'dummy-input-prefix',
'--weather-model', 'ERA5',
]

@contextmanager
def make_gunw_path():
with TemporaryDirectory() as tempdir:
shutil.copy(EXAMPLE_GUNW_PATH, tempdir)
yield Path(tempdir) / Path(EXAMPLE_GUNW_PATH).name

@contextmanager
def make_json_data_path():
with TemporaryDirectory() as tempdir:
shutil.copy(EXAMPLE_JSON_DATA_PATH, tempdir)
yield Path(tempdir) / Path(EXAMPLE_JSON_DATA_PATH).name


# Patch aws.get_s3_file to produce None then check for the correct error message
def test_missing_product_file(mocker, iargs):
side_effect = [
None, # GUNW product file
# program should fail
]
mocker.patch('RAiDER.aws.get_s3_file', side_effect=side_effect)
with pytest.raises(ValueError) as excinfo:
RAiDER.cli.raider.calcDelaysGUNW(iargs)
assert "GUNW product file could not be found" in str(excinfo.value)


# Patch aws.get_s3_file to produce None then check for the correct error message
def test_missing_metadata_file(mocker, iargs):
with make_gunw_path() as gunw_path:
side_effect = [
gunw_path, # GUNW product file
None, # GUNW metadata file
# program should fail
]
mocker.patch('RAiDER.aws.get_s3_file', side_effect=side_effect)
with pytest.raises(ValueError) as excinfo:
RAiDER.cli.raider.calcDelaysGUNW(iargs)
assert "GUNW metadata file could not be found" in str(excinfo.value)


def test_missing_browse_image(mocker, iargs):
with make_gunw_path() as gunw_path, make_json_data_path() as json_data_path:
side_effect = [
gunw_path, # GUNW product file
json_data_path, # GUNW metadata file
None, # GUNW browse image
# program should fail
]
mocker.patch('RAiDER.aws.get_s3_file', side_effect=side_effect)
with pytest.raises(ValueError) as excinfo:
RAiDER.cli.raider.calcDelaysGUNW(iargs)
assert "GUNW browse image could not be found" in str(excinfo.value)
14 changes: 8 additions & 6 deletions tools/RAiDER/aws.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Optional, Union
from mimetypes import guess_type
from pathlib import Path
from typing import Union

import boto3

Expand All @@ -9,7 +9,7 @@
S3_CLIENT = boto3.client('s3')


def get_tag_set() -> dict:
def get_tag_set():
tag_set = {
'TagSet': [
{
Expand All @@ -28,7 +28,7 @@ def get_content_type(file_location: Union[Path, str]) -> str:
return content_type


def upload_file_to_s3(path_to_file: Union[str, Path], bucket: str, prefix: str = ''):
def upload_file_to_s3(path_to_file: Union[str, Path], bucket: str, prefix: str = '') -> None:
path_to_file = Path(path_to_file)
key = str(Path(prefix) / path_to_file)
extra_args = {'ContentType': get_content_type(key)}
Expand All @@ -41,13 +41,15 @@ def upload_file_to_s3(path_to_file: Union[str, Path], bucket: str, prefix: str =
S3_CLIENT.put_object_tagging(Bucket=bucket, Key=key, Tagging=tag_set)


def get_s3_file(bucket_name, bucket_prefix, file_type: str):
result = S3_CLIENT.list_objects_v2(Bucket=bucket_name, Prefix=bucket_prefix)
def get_s3_file(bucket_name: str, bucket_prefix: str, file_type: str) -> Optional[str]:
result = S3_CLIENT.list_objects_v2(
Bucket=bucket_name,
Prefix=bucket_prefix
)
for s3_object in result['Contents']:
key = s3_object['Key']
if key.endswith(file_type):
file_name = Path(key).name
logger.info(f'Downloading s3://{bucket_name}/{key} to {file_name}')
S3_CLIENT.download_file(bucket_name, key, file_name)
return file_name

Loading

0 comments on commit e37c73c

Please sign in to comment.