Skip to content

Commit c6c99b8

Browse files
mcflugenmdpiper
andauthored
Use an API key when requesting OpenTopography data (#25)
* add api_key keyword to Topography * ignore .open_topography.txt file * return empty string rather than None for file contents * add OpenTopography API key to the test action * add unit tests for finding the api key * Add contributors * add a teeny bit of documentation about api keys * Use opentopography in place of open_topography Squished together is closer to the org name. Co-authored-by: Mark Piper <[email protected]>
1 parent 5e12455 commit c6c99b8

File tree

6 files changed

+116
-1
lines changed

6 files changed

+116
-1
lines changed

.github/workflows/build-test-ci.yml

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ jobs:
4242
run: make install
4343

4444
- name: Test
45+
env:
46+
OPENTOPOGRAPHY_API_KEY: ${{ secrets.OPENTOPOGRAPHY_API_KEY }}
4547
run: |
4648
pytest --cov=bmi_topography --cov-report=xml:./coverage.xml --cov-config=./setup.cfg -vvv
4749
bmi-test bmi_topography:BmiTopography --config-file=./examples/config.yaml --root-dir=./examples -vvv

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,6 @@ dmypy.json
127127

128128
# Pyre type checker
129129
.pyre/
130+
131+
# OpenTopography API key file
132+
.opentopography.txt

CREDITS.md

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ Project lead
66

77
* Mark Piper
88

9+
Contributors
10+
------------
11+
12+
* Eric Hutton
13+
* Mark Piper
14+
915
Acknowledgments
1016
---------------
1117

bmi_topography/cli.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,20 @@
5151
)
5252
@click.option("--no_fetch", is_flag=True, help="Do not fetch data from server.")
5353
def main(quiet, dem_type, south, north, west, east, output_format, no_fetch):
54-
"""Fetch and cache NASA SRTM and JAXA ALOS land elevation data"""
54+
"""Fetch and cache NASA SRTM and JAXA ALOS land elevation data
55+
56+
To fetch some datasets you will need an OpenTopography API key.
57+
You can find instructions on how to obtain one from the OpenTopography
58+
website:
59+
60+
https://opentopography.org/blog/introducing-api-keys-access-opentopography-global-datasets
61+
62+
Once you have received your key, you can pass it to the *bmi-topography*
63+
command in one of two ways:
64+
1. As the environment variable, OPENTOPOGRAPHY_API_KEY.
65+
2. As the contents of an *.opentopography.txt* file located either in
66+
your current directory or you home directory.
67+
"""
5568
topo = Topography(dem_type, south, north, west, east, output_format)
5669
if not no_fetch:
5770
if not quiet:

bmi_topography/topography.py

+33
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Base class to access SRTM elevation data"""
2+
import os
23
import urllib
34
from pathlib import Path
45

@@ -8,6 +9,31 @@
89
from .bbox import BoundingBox
910

1011

12+
def find_api_key():
13+
"""Search for an API key."""
14+
if "OPENTOPOGRAPHY_API_KEY" in os.environ:
15+
api_key = os.environ["OPENTOPOGRAPHY_API_KEY"]
16+
else:
17+
api_key = read_first_of(
18+
[".opentopography.txt", "~/.opentopography.txt"]
19+
).strip()
20+
21+
return api_key
22+
23+
24+
def read_first_of(files):
25+
"""Read the contents of the first file encountered."""
26+
contents = ""
27+
for path in files:
28+
try:
29+
contents = open(path, "r").read()
30+
except OSError:
31+
pass
32+
else:
33+
break
34+
return contents
35+
36+
1137
class Topography:
1238

1339
"""Fetch and cache NASA SRTM land elevation data."""
@@ -38,7 +64,12 @@ def __init__(
3864
east=None,
3965
output_format=None,
4066
cache_dir=None,
67+
api_key=None,
4168
):
69+
if api_key is None:
70+
self._api_key = find_api_key()
71+
else:
72+
self._api_key = api_key
4273

4374
if dem_type in Topography.VALID_DEM_TYPES:
4475
self._dem_type = dem_type
@@ -118,6 +149,8 @@ def fetch(self):
118149
"east": self.bbox.east,
119150
"outputFormat": self.output_format,
120151
}
152+
if self._api_key:
153+
params["API_Key"] = self._api_key
121154

122155
response = requests.get(Topography.data_url(), params=params, stream=True)
123156
response.raise_for_status()

tests/test_api_key.py

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import os
2+
from unittest import mock
3+
4+
from bmi_topography.topography import find_api_key, read_first_of
5+
6+
7+
def copy_environ(exclude=None):
8+
if exclude is None:
9+
exclude = {}
10+
elif isinstance(exclude, str):
11+
exclude = {exclude}
12+
13+
return {key: value for key, value in os.environ.items() if key not in exclude}
14+
15+
16+
def test_find_api_key_not_found():
17+
"""The API key is not given anywhere"""
18+
env = copy_environ(exclude="OPENTOPOGRAPHY_API_KEY")
19+
with mock.patch.dict(os.environ, env, clear=True):
20+
assert find_api_key() == ""
21+
22+
23+
@mock.patch.dict(os.environ, {"OPENTOPOGRAPHY_API_KEY": "foo"})
24+
def test_find_api_key_env(tmpdir):
25+
"""The API key is an environment variable"""
26+
with tmpdir.as_cwd():
27+
with open(".opentopography.txt", "w") as fp:
28+
fp.write("bar")
29+
assert find_api_key() == "foo"
30+
31+
32+
@mock.patch.dict(os.environ, {"OPENTOPOGRAPHY_API_KEY": "foo"})
33+
def test_find_api_key_from_file(tmpdir):
34+
"""The API key is in a file"""
35+
env = copy_environ(exclude="OPENTOPOGRAPHY_API_KEY")
36+
with tmpdir.as_cwd():
37+
with open(".opentopography.txt", "w") as fp:
38+
fp.write("bar")
39+
40+
with mock.patch.dict(os.environ, env, clear=True):
41+
assert find_api_key() == "bar"
42+
43+
44+
def test_read_first_missing(tmpdir):
45+
with tmpdir.as_cwd():
46+
assert read_first_of(["foo.txt"]) == ""
47+
assert read_first_of([]) == ""
48+
49+
50+
def test_read_first_file(tmpdir):
51+
with tmpdir.as_cwd():
52+
with open("foo.txt", "w") as fp:
53+
fp.write("foo")
54+
with open("bar.txt", "w") as fp:
55+
fp.write("bar")
56+
57+
assert read_first_of(["foo.txt", "bar.txt"]) == "foo"
58+
assert read_first_of(["bar.txt", "foo.txt"]) == "bar"

0 commit comments

Comments
 (0)