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

[PT-5198] Workspace singleton #595

Merged
merged 37 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
b9882c1
draft workspace mixin
andher1802 Apr 24, 2024
728e72a
passing pylint
andher1802 Apr 24, 2024
fcacf08
adding workspace_id to workspace
andher1802 Apr 30, 2024
709b631
modeling user not authenticated error.
andher1802 Apr 30, 2024
41376c6
removing from auth the workspace code.
andher1802 Apr 30, 2024
287d9c5
addressing comments and fix tests
andher1802 May 2, 2024
5c2b95e
removing asseet workspace_id from test_auth
andher1802 May 2, 2024
84c4970
restoring authenticate on test initialization and error type fix in t…
andher1802 May 2, 2024
e53b9ce
removing innecessary fixture from test_main
andher1802 May 2, 2024
cb5db6b
Merge branch 'master' into PT5054_WorkspaceMixin
andher1802 May 2, 2024
16eede6
updating changelog and version
andher1802 May 2, 2024
e7222bd
Merge branch 'PT5054_WorkspaceMixin' of github.com:up42/up42-py into …
andher1802 May 2, 2024
a6aa922
merge master branch
andher1802 May 20, 2024
4ab4bef
fix typo
andher1802 May 20, 2024
08b02d9
updating naming workspace class
andher1802 May 21, 2024
3fbf44c
cleaning test auth for workspace class creation
andher1802 May 22, 2024
b3244aa
fixing auth import pylint
andher1802 May 22, 2024
b19da37
restoring __init__.py
andher1802 May 23, 2024
6b0e9f7
Update __init__.py
andher1802 May 23, 2024
6439c2d
Merge branch 'PT5054_WorkspaceMixin' of github.com:up42/up42-py into …
andher1802 May 23, 2024
660dc7c
adding validation to __init__.py
andher1802 May 23, 2024
52d4511
merging master branch
andher1802 May 23, 2024
bab3202
merge master branch
andher1802 May 23, 2024
6801a6d
test __init__
andher1802 May 23, 2024
a6c1891
updating changelog date
andher1802 May 24, 2024
8db8d94
merge master branch
andher1802 May 24, 2024
65b2fba
merge master branch
andher1802 May 24, 2024
85cd826
Simplify test_auth setup
javidq May 24, 2024
9b98357
Merge remote-tracking branch 'origin/PT5054_WorkspaceMixin' into PT50…
javidq May 24, 2024
275fc2f
Simplify auth fixtures and workspace properties
javidq May 24, 2024
595ec2a
Test improvements
javidq May 24, 2024
ef098a3
Authenticate module export
javidq May 24, 2024
165267f
Improve workspace id injection
javidq May 24, 2024
099947c
Clean up
javidq May 24, 2024
5379f51
Apply hooks
javidq May 24, 2024
4d0df36
Version fix
javidq May 24, 2024
c5cb44a
Unused argument precision
javidq May 24, 2024
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ You can check your current version with the following command:
```

For more information, see [UP42 Python package description](https://pypi.org/project/up42-py/).
## 1.0.4a2

**May 24, 2024**

- Added workspace singleton in `main.py`, encapsulating global state (auth, workspace id).
- Inject auth and workspace id instead of passing a containing object.


## 1.0.4a1

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "up42-py"
version = "1.0.4a1"
version = "1.0.4a2"
description = "Python SDK for UP42, the geospatial marketplace and developer platform."
authors = ["UP42 GmbH <support@up42.com>"]
license = "https://github.com/up42/up42-py/blob/master/LICENSE"
Expand Down
14 changes: 2 additions & 12 deletions tests/fixtures/fixtures_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,10 @@

@pytest.fixture
def auth_mock(requests_mock: req_mock.Mocker) -> up42_auth.Auth:
json_get_token = {
token_payload = {
"data": {"accessToken": constants.TOKEN},
"access_token": constants.TOKEN,
"token_type": "bearer",
}
requests_mock.post("https://api.up42.com/oauth/token", json=json_get_token)
requests_mock.get(
url="https://api.up42.com/users/me",
json={"data": {"id": constants.WORKSPACE_ID}},
)
# get_credits_balance
url_get_credits_balance = f"{constants.API_HOST}/accounts/me/credits/balance"
requests_mock.get(
url=url_get_credits_balance,
json=constants.JSON_BALANCE,
)
requests_mock.post("https://api.up42.com/oauth/token", json=token_payload)
return up42_auth.Auth(username=constants.USER_EMAIL, password=constants.PASSWORD)
6 changes: 3 additions & 3 deletions tests/fixtures/fixtures_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def catalog_mock(auth_mock, requests_mock):
json_data_product_schema = json.load(json_file)
requests_mock.get(url=url_data_product_schema, json=json_data_product_schema)

return catalog.Catalog(auth=auth_mock)
return catalog.Catalog(auth=auth_mock, workspace_id=constants.WORKSPACE_ID)


@pytest.fixture()
Expand Down Expand Up @@ -77,7 +77,7 @@ def catalog_pagination_mock(auth_mock, requests_mock):
[{"json": search_response_json}, {"json": pagination_response_json}],
)

return catalog.Catalog(auth=auth_mock)
return catalog.Catalog(auth=auth_mock, workspace_id=constants.WORKSPACE_ID)


@pytest.fixture()
Expand Down Expand Up @@ -119,4 +119,4 @@ def catalog_usagetype_mock(auth_mock, requests_mock):
],
)

return catalog.Catalog(auth=auth_mock)
return catalog.Catalog(auth=auth_mock, workspace_id=constants.WORKSPACE_ID)
2 changes: 0 additions & 2 deletions tests/fixtures/fixtures_globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,5 +269,3 @@
"updatedAt": "2022-06-20T04:05:31.755744Z",
}
}

JSON_BALANCE = {"data": {"balance": 10693}}
2 changes: 1 addition & 1 deletion tests/fixtures/fixtures_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ def storage_mock(auth_mock, requests_mock):
# orders info
url_order_info = f"{constants.API_HOST}/v2/orders/{constants.ORDER_ID}"
requests_mock.get(url=url_order_info, json=constants.JSON_ORDER)
return storage.Storage(auth=auth_mock)
return storage.Storage(auth=auth_mock, workspace_id=constants.WORKSPACE_ID)
6 changes: 3 additions & 3 deletions tests/fixtures/fixtures_tasking.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def tasking_mock(auth_mock, requests_mock):
json=accepted_id_response_json,
)

return tasking.Tasking(auth=auth_mock)
return tasking.Tasking(auth=auth_mock, workspace_id=constants.WORKSPACE_ID)


@pytest.fixture()
Expand Down Expand Up @@ -148,7 +148,7 @@ def tasking_get_feasibility_mock(auth_mock, requests_mock):
json_data = json.load(json_file)
requests_mock.get(url=get_feasibility_decision_param, json=json_data)

return tasking.Tasking(auth=auth_mock)
return tasking.Tasking(auth=auth_mock, workspace_id=constants.WORKSPACE_ID)


@pytest.fixture()
Expand All @@ -168,4 +168,4 @@ def tasking_choose_feasibility_mock(auth_mock, requests_mock):
"detail": {},
}
requests_mock.patch(url=choose_feasibility_url, status_code=405, json=response)
return tasking.Tasking(auth=auth_mock)
return tasking.Tasking(auth=auth_mock, workspace_id=constants.WORKSPACE_ID)
20 changes: 12 additions & 8 deletions tests/fixtures/fixtures_webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
@pytest.fixture
def webhook_mock(auth_mock, requests_mock):
# webhook info
url_webhook_info = f"{constants.API_HOST}/workspaces/{auth_mock.workspace_id}/webhooks/{constants.WEBHOOK_ID}"
url_webhook_info = f"{constants.API_HOST}/workspaces/{constants.WORKSPACE_ID}/webhooks/{constants.WEBHOOK_ID}"
requests_mock.get(url=url_webhook_info, json=constants.JSON_WEBHOOK)

# test event
url_test_event = f"{constants.API_HOST}/workspaces/{auth_mock.workspace_id}/webhooks/{constants.WEBHOOK_ID}/tests"
url_test_event = f"{constants.API_HOST}/workspaces/{constants.WORKSPACE_ID}/webhooks/{constants.WEBHOOK_ID}/tests"
json_test_event = {
"data": {
"startedAt": "2022-06-20T04:33:48.770826Z",
Expand All @@ -23,14 +23,18 @@ def webhook_mock(auth_mock, requests_mock):
requests_mock.post(url=url_test_event, json=json_test_event)

# update
url_update = f"{constants.API_HOST}/workspaces/{auth_mock.workspace_id}/webhooks/{constants.WEBHOOK_ID}"
url_update = f"{constants.API_HOST}/workspaces/{constants.WORKSPACE_ID}/webhooks/{constants.WEBHOOK_ID}"
requests_mock.put(url=url_update, json=constants.JSON_WEBHOOK)

# delete
url_delete = f"{constants.API_HOST}/workspaces/{auth_mock.workspace_id}/webhooks/{constants.WEBHOOK_ID}"
url_delete = f"{constants.API_HOST}/workspaces/{constants.WORKSPACE_ID}/webhooks/{constants.WEBHOOK_ID}"
requests_mock.delete(url=url_delete)

return webhooks.Webhook(auth=auth_mock, webhook_id=constants.WEBHOOK_ID)
return webhooks.Webhook(
auth=auth_mock,
workspace_id=constants.WORKSPACE_ID,
webhook_id=constants.WEBHOOK_ID,
)


@pytest.fixture
Expand All @@ -41,7 +45,7 @@ def webhooks_mock(auth_mock, requests_mock):
requests_mock.get(url=url_events, json=events_json)

# get webhooks
url_webhooks = f"{constants.API_HOST}/workspaces/{auth_mock.workspace_id}/webhooks"
url_webhooks = f"{constants.API_HOST}/workspaces/{constants.WORKSPACE_ID}/webhooks"
webhooks_json = {
"data": [
{
Expand Down Expand Up @@ -70,7 +74,7 @@ def webhooks_mock(auth_mock, requests_mock):
requests_mock.get(url=url_webhooks, json=webhooks_json)

# create webhook
url_create_webhook = f"{constants.API_HOST}/workspaces/{auth_mock.workspace_id}/webhooks"
url_create_webhook = f"{constants.API_HOST}/workspaces/{constants.WORKSPACE_ID}/webhooks"
requests_mock.post(url=url_create_webhook, json=constants.JSON_WEBHOOK)

return webhooks.Webhooks(auth=auth_mock)
return webhooks.Webhooks(auth=auth_mock, workspace_id=constants.WORKSPACE_ID)
67 changes: 26 additions & 41 deletions tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

CONFIG_FILE = "some-config-file"
TOKEN_ENDPOINT = constants.API_HOST + "/oauth/token"
WORKSPACE_ENDPOINT = constants.API_HOST + "/users/me"
URL = constants.API_HOST + "/some-url"
RESPONSE_TEXT = "some-response-text"
ERROR = {"some": "error"}
Expand Down Expand Up @@ -67,37 +66,29 @@ def test_should_collect_credentials(self):
client.session = session


def create_auth(requests_mock: req_mock.Mocker):
credential_sources = [{"some": "credentials"}]
get_sources = mock.MagicMock(return_value=credential_sources)
create_client = mock.MagicMock(return_value=client)

requests_mock.get(
WORKSPACE_ENDPOINT,
json={"data": {"id": constants.WORKSPACE_ID}},
)
auth = up42_auth.Auth(
cfg_file=CONFIG_FILE,
username=constants.USER_EMAIL,
password=constants.PASSWORD,
get_credential_sources=get_sources,
create_client=create_client,
)

get_sources.assert_called_once_with(
CONFIG_FILE,
constants.USER_EMAIL,
constants.PASSWORD,
)
create_client.assert_called_once_with(credential_sources, TOKEN_ENDPOINT)
return auth
class TestAuth:
def setup_method(self, _):
credential_sources = [{"some": "credentials"}]
get_sources = mock.MagicMock(return_value=credential_sources)
create_client = mock.MagicMock(return_value=client)

self.auth = up42_auth.Auth(
cfg_file=CONFIG_FILE,
username=constants.USER_EMAIL,
password=constants.PASSWORD,
get_credential_sources=get_sources,
create_client=create_client,
)

get_sources.assert_called_once_with(
CONFIG_FILE,
constants.USER_EMAIL,
constants.PASSWORD,
)
create_client.assert_called_once_with(credential_sources, TOKEN_ENDPOINT)

class TestAuth:
def test_should_authenticate_when_created(self, requests_mock: req_mock.Mocker):
auth = create_auth(requests_mock)
assert auth.workspace_id == constants.WORKSPACE_ID
assert auth.session == session
def test_should_authenticate_when_created(self):
assert self.auth.session == session

@pytest.mark.parametrize(
"expected",
Expand All @@ -114,45 +105,40 @@ def test_should_pass_dict_for_json_response(
expected: dict,
requests_mock: req_mock.Mocker,
):
auth = create_auth(requests_mock)
requests_mock.request(
http_method,
URL,
request_headers=REQUEST_HEADERS,
json=expected,
additional_matcher=match_request_body(request_data),
)
assert auth.request(http_method, URL, request_data) == expected
assert self.auth.request(http_method, URL, request_data) == expected

def test_should_pass_text_for_text_response(
self, http_method: str, request_data: dict, requests_mock: req_mock.Mocker
):
auth = create_auth(requests_mock)
requests_mock.request(
http_method,
URL,
request_headers=REQUEST_HEADERS,
text=RESPONSE_TEXT,
additional_matcher=match_request_body(request_data),
)
assert auth.request(http_method, URL, request_data) == RESPONSE_TEXT
assert self.auth.request(http_method, URL, request_data) == RESPONSE_TEXT

def test_should_pass_response(self, http_method: str, request_data: dict, requests_mock: req_mock.Mocker):
auth = create_auth(requests_mock)
requests_mock.request(
http_method,
URL,
request_headers=REQUEST_HEADERS,
text=RESPONSE_TEXT,
additional_matcher=match_request_body(request_data),
)
response = auth.request(http_method, URL, request_data, return_text=False)
response = self.auth.request(http_method, URL, request_data, return_text=False)
assert isinstance(response, requests.Response)
assert response.text == RESPONSE_TEXT

def test_fails_if_v1_api_request_fails(self, http_method: str, request_data: dict, requests_mock: req_mock.Mocker):
auth = create_auth(requests_mock)

requests_mock.request(
http_method,
URL,
Expand All @@ -161,7 +147,7 @@ def test_fails_if_v1_api_request_fails(self, http_method: str, request_data: dic
additional_matcher=match_request_body(request_data),
)
with pytest.raises(ValueError) as exc_info:
auth.request(http_method, URL, request_data)
self.auth.request(http_method, URL, request_data)

assert str(exc_info.value) == str(ERROR)

Expand All @@ -175,7 +161,6 @@ def test_fails_if_status_code_is_bad(
error: Optional[Dict],
requests_mock: req_mock.Mocker,
):
auth = create_auth(requests_mock)
requests_mock.request(
http_method,
URL,
Expand All @@ -185,5 +170,5 @@ def test_fails_if_status_code_is_bad(
additional_matcher=match_request_body(request_data),
)
with pytest.raises(requests.HTTPError) as exc_info:
auth.request(http_method, URL, request_data, return_text=return_text)
self.auth.request(http_method, URL, request_data, return_text=return_text)
assert str(exc_info.value) == (json.dumps(error) if error else "")
2 changes: 1 addition & 1 deletion tests/test_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ def test_construct_order_parameters(catalog_mock):

# pylint: disable=unused-argument
def test_estimate_order_from_catalog(catalog_order_parameters, requests_mock, auth_mock):
catalog_instance = catalog.Catalog(auth=auth_mock)
catalog_instance = catalog.Catalog(auth=auth_mock, workspace_id=constants.WORKSPACE_ID)
expected_payload = {
"summary": {"totalCredits": 100, "totalSize": 0.1, "unit": "SQ_KM"},
"results": [{"index": 0, "credits": 100, "unit": "SQ_KM", "size": 0.1}],
Expand Down
61 changes: 27 additions & 34 deletions tests/test_initialization.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,43 @@
from unittest import mock

import pytest

import up42
from up42 import catalog, main, tasking
from up42 import catalog, main, storage, tasking

from .fixtures import fixtures_globals as constants


def test_initialize_object_without_auth_raises():
main._auth = None # pylint: disable=protected-access

with pytest.raises(RuntimeError):
def test_fails_to_initialize_if_not_authenticated():
with pytest.raises(main.UserNotAuthenticated):
up42.initialize_catalog()
with pytest.raises(RuntimeError):
with pytest.raises(main.UserNotAuthenticated):
up42.initialize_storage()
with pytest.raises(RuntimeError):
with pytest.raises(main.UserNotAuthenticated):
up42.initialize_order(order_id=constants.ORDER_ID)
with pytest.raises(RuntimeError):
with pytest.raises(main.UserNotAuthenticated):
up42.initialize_asset(asset_id=constants.ASSET_ID)


def test_global_auth_initialize_objects(
storage_mock,
def test_should_initialize_objects(
auth_mock,
order_mock,
asset_mock,
):
up42.authenticate(username=constants.USER_EMAIL, password=constants.PASSWORD)
catalog_obj = up42.initialize_catalog()
assert isinstance(catalog_obj, catalog.Catalog)
storage_obj = up42.initialize_storage()
assert storage_obj.workspace_id == storage_mock.workspace_id
order_obj = up42.initialize_order(order_id=constants.ORDER_ID)
assert order_obj.info == order_mock.info
asset_obj = up42.initialize_asset(asset_id=constants.ASSET_ID)
assert asset_obj.info == asset_mock.info


@pytest.fixture(autouse=True)
def setup_workspace(requests_mock):
requests_mock.post("https://api.up42.com/oauth/token", json={"access_token": constants.TOKEN})
requests_mock.get(
url="https://api.up42.com/users/me",
json={"data": {"id": constants.WORKSPACE_ID}},
)


def test_should_initialize_tasking():
up42.authenticate(username=constants.USER_EMAIL, password=constants.PASSWORD)
result = up42.initialize_tasking()
assert isinstance(result, tasking.Tasking)
with mock.patch("up42.main.workspace") as workspace_mock:
workspace_mock.id = constants.WORKSPACE_ID
workspace_mock.auth = auth_mock

catalog_obj = up42.initialize_catalog()
assert isinstance(catalog_obj, catalog.Catalog)

storage_obj = up42.initialize_storage()
assert isinstance(storage_obj, storage.Storage)
assert storage_obj.workspace_id == constants.WORKSPACE_ID

order_obj = up42.initialize_order(order_id=constants.ORDER_ID)
assert order_obj.info == order_mock.info
asset_obj = up42.initialize_asset(asset_id=constants.ASSET_ID)
assert asset_obj.info == asset_mock.info
result = up42.initialize_tasking()
assert isinstance(result, tasking.Tasking)
Loading