Skip to content
Open
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
16 changes: 11 additions & 5 deletions dataplicity_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3017,9 +3017,9 @@ def devices_history_delete(
_require_auth(state)
resolved_hash = _resolve_device_hash_interactive(state, device_hash, action_name="history delete")
endpoint = f"/api/developer/devices/{resolved_hash}/timeline/{event_id}/"
_debug_api_request(state, method="DELETE", path=endpoint)
_debug_api_request(state, method="PATCH", path=endpoint)
response = state.api.request(
"DELETE",
"PATCH",
endpoint,
)
if not response.ok:
Expand Down Expand Up @@ -3143,7 +3143,7 @@ def devices_connection_quality(ctx: typer.Context, device_hash: Optional[str] =
payload = status_response.data
else:
detail_response = state.api.get(f"/api/developer/devices/{resolved_hash}/")
host_response = state.api.get(f"/api/remote/devices/{resolved_hash}/host/")
host_response = state.api.get(_remote_device_api_path(state, resolved_hash, "host"))
if detail_response.ok and isinstance(detail_response.data, dict):
detail = detail_response.data
payload = {
Expand Down Expand Up @@ -3278,13 +3278,19 @@ def devices_provisioning_key(
state.console.print(message)


def _remote_device_api_path(state: AppContext, device_hash: str, resource: str) -> str:
"""Organisation keys and logged-in users use distinct remote-access planes."""
prefix = 'developer' if state.config.auth_method == 'api_key' else 'remote'
return f"/api/{prefix}/devices/{device_hash}/{resource}/"


async def _resolve_m2m_url(
state: AppContext,
device_hash: str,
*,
request_timeout: Optional[int] = None,
) -> str:
path = f"/api/remote/devices/{device_hash}/host/"
path = _remote_device_api_path(state, device_hash, "host")
if request_timeout is None:
response = state.api.get(path)
else:
Expand All @@ -3308,7 +3314,7 @@ async def _resolve_m2m_url(

def _open_remote_access_port(state: AppContext, device_hash: str, payload: Dict[str, Any]) -> Any:
return state.api.post(
f"/api/remote/devices/{device_hash}/ports/",
_remote_device_api_path(state, device_hash, "ports"),
json_data=payload,
)

Expand Down
2 changes: 1 addition & 1 deletion scripts/port_forward_mock_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ async def route_http(
]
}
).encode("utf-8"), "application/json"
if method == "GET" and path.startswith("/api/developer/devices/"):
if method == "GET" and path == f"/api/developer/devices/{DEVICE_HASH}":
return 200, json.dumps(
{
"hash": DEVICE_HASH,
Expand Down
2 changes: 1 addition & 1 deletion tests/test_devices_connection_quality.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def fake_get(_self, path: str, *, params=None): # type: ignore[no-untyped-def]
return ApiResponse(False, 404, {"detail": "Not found."}, '{"detail":"Not found."}')
if path == f"/api/developer/devices/{self.device_hash}/":
return ApiResponse(True, 200, {"status": "online", "last_heartbeat": 1719559200000}, "")
if path == f"/api/remote/devices/{self.device_hash}/host/":
if path == f"/api/developer/devices/{self.device_hash}/host/":
return ApiResponse(True, 200, {"m2m_url": "wss://m2m.dataplicity.com/m2m/"}, "")
raise AssertionError(f"unexpected GET path: {path}")

Expand Down
2 changes: 1 addition & 1 deletion tests/test_devices_history_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def fake_request(
allow_refresh=True,
): # type: ignore[no-untyped-def]
_ = (params, json_data, data, headers, timeout, allow_refresh)
self.assertEqual(method, "DELETE")
self.assertEqual(method, "PATCH")
self.assertEqual(path, f"/api/developer/devices/{self.device_hash}/timeline/777/")
return ApiResponse(True, 200, deleted_payload, json.dumps(deleted_payload))

Expand Down
18 changes: 18 additions & 0 deletions tests/test_gateway_remote_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import asyncio
import unittest
from types import SimpleNamespace
from unittest.mock import Mock
from dataplicity_cli.cli import _resolve_m2m_url, _open_remote_access_port


class GatewayRemoteAuthTests(unittest.TestCase):
def test_requests_select_the_authenticated_plane(self):
for method, prefix in [('api_key', 'developer'), ('jwt', 'remote')]:
api = Mock()
api.get.return_value = SimpleNamespace(ok=True, data={'m2m_url': 'https://relay.example/m2m/'})
state = SimpleNamespace(config=SimpleNamespace(auth_method=method), api=api)
url = asyncio.run(_resolve_m2m_url(state, 'device'))
self.assertIn('device=device', url)
api.get.assert_called_once_with(f'/api/{prefix}/devices/device/host/')
_open_remote_access_port(state, 'device', {'service': 'terminal'})
api.post.assert_called_once_with(f'/api/{prefix}/devices/device/ports/', json_data={'service': 'terminal'})
Loading