Skip to content

Commit

Permalink
Issue #72: move "at least 0.4.0 api" version check to Connection cons…
Browse files Browse the repository at this point in the history
…tructor
  • Loading branch information
soxofaan committed Nov 18, 2019
1 parent 028025e commit e27903a
Show file tree
Hide file tree
Showing 13 changed files with 98 additions and 87 deletions.
7 changes: 5 additions & 2 deletions openeo/capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ def api_version(self):
return

@property
def api_version_check(self):
def api_version_check(self) -> 'ComparableVersion':
"""Helper to easily check if the API version is at least or below some threshold version."""
return ComparableVersion(self.api_version())
api_version = self.api_version()
if not api_version:
raise ApiVersionException("No API version found")
return ComparableVersion(api_version)

def list_features(self):
""" List all supported features / endpoints."""
Expand Down
43 changes: 16 additions & 27 deletions openeo/rest/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from requests.auth import HTTPBasicAuth, AuthBase

import openeo
from openeo.capabilities import Capabilities
from openeo.capabilities import Capabilities, ApiVersionException, ComparableVersion
from openeo.imagecollection import CollectionMetadata
from openeo.rest.auth.auth import NullAuth, BearerAuth
from openeo.rest.imagecollectionclient import ImageCollectionClient
Expand Down Expand Up @@ -153,6 +153,8 @@ class Connection(RestApiConnection):
Connection to an openEO backend.
"""

_MINIMUM_API_VERSION = ComparableVersion("0.4.0")

def __init__(self, url, auth: AuthBase = None, session: requests.Session = None):
"""
Constructor of Connection, authenticates user.
Expand All @@ -161,6 +163,12 @@ def __init__(self, url, auth: AuthBase = None, session: requests.Session = None)
super().__init__(root_url=url, auth=auth, session=session)
self._cached_capabilities = None

# Initial API version check.
if self._api_version.below(self._MINIMUM_API_VERSION):
raise ApiVersionException("OpenEO API version should be at least {m!s}, but got {v!s}".format(
m=self._MINIMUM_API_VERSION, v= self._api_version)
)

def authenticate_basic(self, username: str, password: str) -> 'Connection':
"""
Authenticate a user to the backend using basic username and password.
Expand Down Expand Up @@ -227,8 +235,7 @@ def list_collection_ids(self) -> List[str]:
Get list of all collection ids
:return: list of collection ids
"""
field = 'id' if self._api_version.at_least('0.4.0') else 'name'
return [collection[field] for collection in self.list_collections() if field in collection]
return [collection['id'] for collection in self.list_collections() if 'id' in collection]

def capabilities(self) -> 'Capabilities':
"""
Expand Down Expand Up @@ -304,7 +311,7 @@ def list_processgraphs(self, process_graph):
raise NotImplementedError()

@property
def _api_version(self):
def _api_version(self) -> ComparableVersion:
return self.capabilities().api_version_check

def load_collection(self, collection_id: str, **kwargs) -> ImageCollectionClient:
Expand Down Expand Up @@ -365,7 +372,7 @@ def create_file(self, path):
raise NotImplementedError()

# TODO: Maybe rename to execute and merge with execute().
def download(self, graph, outputfile, format_options):
def download(self, graph, outputfile):
"""
Downloads the result of a process graph synchronously, and save the result to the given file.
This method is useful to export binary content such as images. For json content, the execute method is recommended.
Expand All @@ -375,16 +382,8 @@ def download(self, graph, outputfile, format_options):
:param format_options: formating options
:return: job_id: String
"""
path = "/preview"
request = {
"process_graph": graph
}
if self._api_version.at_least('0.4.0'):
path = "/result"
else:
request["output"] = format_options

download_url = self.build_url(path)
request = {"process_graph": graph}
download_url = self.build_url("/result")
r = self.post(download_url, json=request, stream=True, timeout=1000)
with pathlib.Path(outputfile).open(mode="wb") as f:
shutil.copyfileobj(r.raw, f)
Expand All @@ -399,11 +398,7 @@ def execute(self, process_graph, output_format, output_parameters=None, budget=N
:return: job_id: String
"""
# TODO: add output_format to execution
path = "/preview"
if self._api_version.at_least('0.4.0'):
path = "/result"
response = self.post(path, process_graph)
return self.parse_json_response(response)
return self.post(path="/result", json=process_graph).json()

def create_job(self, process_graph:Dict, output_format:str=None, output_parameters:Dict={},
title:str=None, description:str=None, plan:str=None, budget=None,
Expand All @@ -427,16 +422,9 @@ def create_job(self, process_graph:Dict, output_format:str=None, output_paramete
"budget": budget
}

if not self._api_version.at_least('0.4.0'):
process_graph["output"] = {
"format": output_format,
"parameters": output_parameters
}

job_status = self.post("/jobs", process_graph)

job = None

if job_status.status_code == 201:
job_info = job_status.headers._store
if "openeo-identifier" in job_info:
Expand All @@ -456,6 +444,7 @@ def parse_json_response(self, response: requests.Response):
:param response: Response of a RESTful request
:return: response: JSON Response
"""
# TODO Deprecated: status handling is now in RestApiConnection
if response.status_code == 200 or response.status_code == 201:
return response.json()
else:
Expand Down
7 changes: 2 additions & 5 deletions openeo/rest/imagecollectionclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ def load_collection(
:return:
"""
# TODO: rename function to load_collection for better similarity with corresponding process id?
assert session.capabilities().api_version_check.at_least('0.4.0')
builder = GraphBuilder()
process_id = 'load_collection'
arguments = {
Expand Down Expand Up @@ -82,8 +81,6 @@ def load_disk_collection(cls, session: 'Connection', file_format: str, glob_patt
:param options: options specific to the file format
:return: the data as an ImageCollection
"""
assert session.capabilities().api_version_check.at_least('0.4.0')

builder = GraphBuilder()

process_id = 'load_disk_data'
Expand Down Expand Up @@ -968,10 +965,10 @@ def download(self, outputfile: str, **format_options) -> str:
raise ValueError("Please use the 'format' keyword argument to specify the output format. Use openeo.connection.Connection#list_file_types to retrieve available ouput formats for this backend.")
newcollection = self.graph_add_process("save_result",args)
newcollection.graph[newcollection.node_id]["result"] = True
return self.session.download(newcollection.graph, outputfile, format_options)
return self.session.download(newcollection.graph, outputfile)
else:
self.graph[self.node_id]["result"] = True
return self.session.download(self.graph, outputfile, format_options)
return self.session.download(self.graph, outputfile)

def tiled_viewing_service(self,**kwargs) -> Dict:
newbuilder = self.builder.copy()
Expand Down
18 changes: 11 additions & 7 deletions tests/rest/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ def test_connection_with_session():
session = mock.Mock()
response = session.request.return_value
response.status_code = 200
response.json.return_value = {"foo": "bar"}
response.json.return_value = {"foo": "bar", "api_version": "0.4.0"}
conn = Connection("https://oeo.net/", session=session)
assert conn.capabilities().capabilities == {"foo": "bar"}
assert conn.capabilities().capabilities["foo"] == "bar"
session.request.assert_any_call(
url="https://oeo.net/", method="get", headers=mock.ANY, stream=mock.ANY, auth=mock.ANY
)
Expand All @@ -64,15 +64,16 @@ def test_connect_with_session():
session = mock.Mock()
response = session.request.return_value
response.status_code = 200
response.json.return_value = {"foo": "bar"}
response.json.return_value = {"foo": "bar", "api_version": "0.4.0"}
conn = connect("https://oeo.net/", session=session)
assert conn.capabilities().capabilities == {"foo": "bar"}
assert conn.capabilities().capabilities["foo"] == "bar"
session.request.assert_any_call(
url="https://oeo.net/", method="get", headers=mock.ANY, stream=mock.ANY, auth=mock.ANY
)


def test_api_error(requests_mock):
requests_mock.get('https://oeo.net/', json={"api_version": "0.4.0"})
conn = Connection(API_URL)
requests_mock.get('https://oeo.net/collections/foobar', status_code=404, json={
"code": "CollectionNotFound", "message": "No such things as a collection 'foobar'", "id": "54321"
Expand All @@ -88,6 +89,7 @@ def test_api_error(requests_mock):


def test_api_error_non_json(requests_mock):
requests_mock.get('https://oeo.net/', json={"api_version": "0.4.0"})
conn = Connection(API_URL)
requests_mock.get('https://oeo.net/collections/foobar', status_code=500, text="olapola")
with pytest.raises(OpenEoApiError) as exc_info:
Expand All @@ -101,25 +103,27 @@ def test_api_error_non_json(requests_mock):


def test_authenticate_basic(requests_mock):
requests_mock.get(API_URL, json={"api_version": "0.4.0"})
conn = Connection(API_URL)

def text_callback(request, context):
assert request.headers["Authorization"] == "Basic am9objpqMGhu"
return '{"access_token":"w3lc0m3"}'

requests_mock.get('https://oeo.net/credentials/basic', text=text_callback)
requests_mock.get(API_URL + 'credentials/basic', text=text_callback)

assert isinstance(conn.auth, NullAuth)
conn.authenticate_basic(username="john", password="j0hn")
assert isinstance(conn.auth, BearerAuth)
assert conn.auth.bearer == "w3lc0m3"


def test_authenticate_oidc(oidc_test_setup):
def test_authenticate_oidc(oidc_test_setup, requests_mock):
# see test/rest/conftest.py for `oidc_test_setup` fixture
client_id = "myclient"
oidc_discovery_url = "https://oeo.net/credentials/oidc"
state, webbrowser_open = oidc_test_setup(client_id=client_id, oidc_discovery_url=oidc_discovery_url)
requests_mock.get(API_URL, json={"api_version": "0.4.0"})

# With all this set up, kick off the openid connect flow
conn = Connection(API_URL)
Expand All @@ -130,8 +134,8 @@ def test_authenticate_oidc(oidc_test_setup):


def test_load_collection_arguments(requests_mock):
requests_mock.get(API_URL, json={"api_version": "0.4.0"})
conn = Connection(API_URL)
requests_mock.get(API_URL, json={"version": "0.4.0"})
requests_mock.get(API_URL + "collections/FOO", json={
"properties": {"eo:bands": [{"name": "red"}, {"name": "green"}, {"name": "blue"}]}
})
Expand Down
2 changes: 1 addition & 1 deletion tests/rest/test_imagecollectionclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@

@pytest.fixture
def session040(requests_mock):
requests_mock.get(API_URL + "/", json={"api_version": "0.4.0"})
session = openeo.connect(API_URL)
requests_mock.get(API_URL + "/", json={"version": "0.4.0"})
return session


Expand Down
10 changes: 5 additions & 5 deletions tests/test_bandmath.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
class TestBandMath(TestCase):

def test_basic(self, m):
m.get("http://localhost:8000/api/", json={"api_version": "0.4.0"})
session = openeo.connect("http://localhost:8000/api")
session.post = MagicMock()
session.download = MagicMock()

m.get("http://localhost:8000/api/", json={"version": "0.4.0"})
m.get("http://localhost:8000/api/collections/SENTINEL2_RADIOMETRY_10M", json={"product_id": "sentinel2_subset",
"bands": [{'band_id': 'B02'},
{'band_id': 'B04'},
Expand All @@ -32,11 +32,11 @@ def test_basic(self, m):
assert cube.band('B02').graph == expected_graph

def test_band_indexing(self, m):
m.get("http://localhost:8000/api/", json={"api_version": "0.4.0"})
session = openeo.connect("http://localhost:8000/api")
session.post = MagicMock()
session.download = MagicMock()

m.get("http://localhost:8000/api/", json={"version": "0.4.0"})
m.get("http://localhost:8000/api/collections/CGS_SENTINEL2_RADIOMETRY_V102_001", json={
"id": "CGS_SENTINEL2_RADIOMETRY_V102_001",
"properties": {
Expand All @@ -58,11 +58,11 @@ def test_band_indexing(self, m):

def test_evi(self,m):
# configuration phase: define username, endpoint, parameters?
m.get("http://localhost:8000/api/", json={"api_version": "0.4.0"})
session = openeo.connect("http://localhost:8000/api")
session.post = MagicMock()
session.download = MagicMock()

m.get("http://localhost:8000/api/", json={"version": "0.4.0"})
m.get("http://localhost:8000/api/collections", json={"collections": [{"product_id": "sentinel2_subset"}]})
m.get("http://localhost:8000/api/collections/SENTINEL2_RADIOMETRY_10M", json={"product_id": "sentinel2_subset",
"bands": [{'band_id': 'B02'},
Expand Down Expand Up @@ -94,11 +94,11 @@ def test_evi(self,m):

def test_ndvi_udf(self, m):
#configuration phase: define username, endpoint, parameters?
m.get("http://localhost:8000/api/", json={"version": "0.4.1"})
session = openeo.connect("http://localhost:8000/api")
session.post = MagicMock()
session.download = MagicMock()

m.get("http://localhost:8000/api/", json={"version": "0.4.1"})
m.get("http://localhost:8000/api/collections", json={"collections": [{"product_id": "sentinel2_subset"}]})
m.get("http://localhost:8000/api/collections/SENTINEL2_RADIOMETRY_10M", json={"product_id": "sentinel2_subset",
"bands": [{'band_id': 'B0'},
Expand Down Expand Up @@ -154,9 +154,9 @@ def test_ndvi_udf(self, m):

def test_ndvi_udf_0_4_0(self, m):
#configuration phase: define username, endpoint, parameters?
m.get("http://localhost:8000/api/", json={"api_version": "0.4.0"})
session = openeo.connect("http://localhost:8000/api")

m.get("http://localhost:8000/api/", json={"version": "0.4.0"})
m.get("http://localhost:8000/api/collections", json={"collections": [{"product_id": "sentinel2_subset"}]})
m.get("http://localhost:8000/api/collections/SENTINEL2_RADIOMETRY_10M", json={"product_id": "sentinel2_subset",
"bands": [{'band_id': 'B0'},
Expand Down
2 changes: 1 addition & 1 deletion tests/test_batch_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
class TestBatchJobs(TestCase):

def test_create_job(self,m):
m.get("http://localhost:8000/api/", json={"api_version": "0.4.0"})
session = openeo.connect("http://localhost:8000/api")
#session.post = MagicMock()
session.download = MagicMock()

m.get("http://localhost:8000/api/", json={"version": "0.4.0"})
m.get("http://localhost:8000/api/collections", json={"collections": [{"product_id": "sentinel2_subset"}]})
m.get("http://localhost:8000/api/collections/SENTINEL2_RADIOMETRY_10M", json={"product_id": "sentinel2_subset",
"bands": [{'band_id': 'B02'},
Expand Down
1 change: 0 additions & 1 deletion tests/test_capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,3 @@ def test_checkable_version():
assert v.above(ComparableVersion('1.2'))
assert v.at_least(ComparableVersion('1.2.3a')) is False
assert v.at_most(ComparableVersion('1.02.03'))

6 changes: 3 additions & 3 deletions tests/test_logical_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ class TestLogicalOps(TestCase):

def test_not_equal(self, m):
# configuration phase: define username, endpoint, parameters?
m.get("http://localhost:8000/api/", json={"api_version": "0.4.0"})
session = openeo.connect("http://localhost:8000/api")
session.post = MagicMock()
session.download = MagicMock()

m.get("http://localhost:8000/api/", json={"version": "0.4.0"})
m.get("http://localhost:8000/api/collections", json={"collections": [{"product_id": "sentinel2_subset"}]})
m.get("http://localhost:8000/api/collections/SENTINEL2_SCF", json={
"product_id": "sentinel2_subset",
Expand All @@ -42,11 +42,11 @@ def test_not_equal(self, m):
assert actual_graph == expected_graph

def test_or(self, m):
m.get("http://localhost:8000/api/", json={"api_version": "0.4.0"})
session = openeo.connect("http://localhost:8000/api")
session.post = MagicMock()
session.download = MagicMock()

m.get("http://localhost:8000/api/", json={"version": "0.4.0"})
m.get("http://localhost:8000/api/collections", json={"collections": [{"product_id": "sentinel2_subset"}]})
m.get("http://localhost:8000/api/collections/SENTINEL2_SCF", json={
"product_id": "sentinel2_subset",
Expand All @@ -65,11 +65,11 @@ def test_or(self, m):
assert actual_graph == expected_graph

def test_and(self, m):
m.get("http://localhost:8000/api/", json={"api_version": "0.4.0"})
session = openeo.connect("http://localhost:8000/api")
session.post = MagicMock()
session.download = MagicMock()

m.get("http://localhost:8000/api/", json={"version": "0.4.0"})
m.get("http://localhost:8000/api/collections", json={"collections": [{"product_id": "sentinel2_subset"}]})
m.get("http://localhost:8000/api/collections/SENTINEL2_SCF", json={
"product_id": "sentinel2_subset",
Expand Down
Loading

0 comments on commit e27903a

Please sign in to comment.