Skip to content

Commit b8b2d5c

Browse files
authored
[Fixes #324] Fixes for GeoNode 5 (#325)
1 parent 88d09a5 commit b8b2d5c

7 files changed

Lines changed: 174 additions & 146 deletions

File tree

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,67 @@
1+
import requests
12
import importlib
23
import typing
34

4-
from ..network import UNSUPPORTED_REMOTE
5-
from packaging import version as packaging_version
6-
from packaging.specifiers import SpecifierSet
5+
SUPPORTED_API_CLIENT = "/api/v2/"
6+
_api_v2_cache: dict[str, bool] = {}
77

8-
SUPPORTED_VERSIONS = SpecifierSet(">=4.0.0, <5.0.0dev0")
98

9+
def is_api_client_supported(base_url: str) -> bool:
10+
"""
11+
Returns True if SUPPORTED_API_CLIENT endpoint responds with HTTP 200
12+
and contains valid JSON.
13+
"""
1014

11-
def validate_version(
12-
version: packaging_version.Version, supported_versions=SUPPORTED_VERSIONS
13-
) -> bool:
15+
if base_url in _api_v2_cache:
16+
return _api_v2_cache[base_url]
1417

15-
version = version.base_version
18+
# delegate to unified helper
19+
supported = _fetch_api_root(base_url) is not None
1620

17-
if version in supported_versions:
18-
return True
19-
else:
20-
return False
21+
_api_v2_cache[base_url] = supported
22+
return supported
23+
24+
25+
def _fetch_api_root(base_url: str) -> dict | None:
26+
"""
27+
Internal helper:
28+
- Makes a request to SUPPORTED_API_CLIENT
29+
- Returns the JSON dict if valid, otherwise None
30+
"""
31+
url = f"{base_url.rstrip('/')}{SUPPORTED_API_CLIENT}"
32+
try:
33+
resp = requests.get(url, timeout=5)
34+
35+
if resp.status_code != 200:
36+
return None
37+
38+
data = resp.json()
39+
return data if isinstance(data, dict) else None
40+
41+
except Exception:
42+
return None
43+
44+
45+
def has_metadata_api(base_url: str) -> bool:
46+
"""
47+
Does the /api/v2/ root include a 'metadata' key?
48+
"""
49+
data = _fetch_api_root(base_url)
50+
return data is not None and "metadata" in data
2151

2252

2353
def get_geonode_client(
2454
connection_settings: "ConnectionSettings",
2555
) -> typing.Optional["BaseGeonodeClient"]:
26-
version = connection_settings.geonode_version
27-
28-
result = None
29-
if version is not None and version != UNSUPPORTED_REMOTE:
30-
class_path = select_supported_client(connection_settings.geonode_version)
31-
if class_path != None:
32-
module_path, class_name = class_path.rpartition(".")[::2]
33-
imported_module = importlib.import_module(module_path)
34-
class_type = getattr(imported_module, class_name)
35-
result = class_type.from_connection_settings(connection_settings)
36-
return result
3756

57+
if not is_api_client_supported(connection_settings.base_url):
58+
return None
3859

39-
def select_supported_client(geonode_version: packaging_version.Version) -> str:
60+
module_path, class_name = select_supported_client().rpartition(".")[::2]
61+
imported_module = importlib.import_module(module_path)
62+
class_type = getattr(imported_module, class_name)
63+
return class_type.from_connection_settings(connection_settings)
4064

41-
result = None
42-
if validate_version(geonode_version):
43-
result = "qgis_geonode.apiclient.geonode_api_v2.GeoNodeApiClient"
4465

45-
return result
66+
def select_supported_client() -> str:
67+
return "qgis_geonode.apiclient.geonode_api_v2.GeoNodeApiClient"

src/qgis_geonode/apiclient/models.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,16 @@ def from_json(cls, contents: str):
263263
],
264264
)
265265

266+
# we need this property for GeoNode 5
267+
@property
268+
def metadata_link(self) -> typing.Optional[str]:
269+
if self.link and self.pk:
270+
# dataset.link: https://my-geonode.org/api/v2/datasets/123
271+
return self.link.replace(
272+
f"/datasets/{self.pk}", f"/metadata/instance/{self.pk}"
273+
)
274+
return None
275+
266276

267277
@dataclasses.dataclass
268278
class GeonodeApiSearchFilters:

src/qgis_geonode/conf.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ class ConnectionSettings:
5656
network_requests_timeout: int = dataclasses.field(
5757
default_factory=_get_network_requests_timeout, init=False
5858
)
59-
geonode_version: typing.Optional[packaging_version.Version] = None
6059
wfs_version: typing.Optional[WfsVersion] = WfsVersion.AUTO
6160
auth_config: typing.Optional[str] = None
6261

@@ -66,18 +65,12 @@ def from_qgs_settings(cls, connection_identifier: str, settings: QgsSettings):
6665
reported_auth_cfg = settings.value("auth_config").strip()
6766
except AttributeError:
6867
reported_auth_cfg = None
69-
raw_geonode_version = settings.value("geonode_version") or UNSUPPORTED_REMOTE
70-
if raw_geonode_version != UNSUPPORTED_REMOTE:
71-
geonode_version = packaging_version.parse(raw_geonode_version)
72-
else:
73-
geonode_version = UNSUPPORTED_REMOTE
7468
return cls(
7569
id=uuid.UUID(connection_identifier),
7670
name=settings.value("name"),
7771
base_url=settings.value("base_url"),
7872
page_size=int(settings.value("page_size", defaultValue=10)),
7973
auth_config=reported_auth_cfg,
80-
geonode_version=geonode_version,
8174
wfs_version=WfsVersion(settings.value("wfs_version", "1.1.0")),
8275
)
8376

@@ -89,9 +82,6 @@ def to_json(self):
8982
"base_url": self.base_url,
9083
"page_size": self.page_size,
9184
"auth_config": self.auth_config,
92-
"geonode_version": str(self.geonode_version)
93-
if self.geonode_version is not None
94-
else None,
9585
"wfs_version": self.wfs_version.value,
9686
}
9787
)
@@ -187,14 +177,6 @@ def save_connection_settings(self, connection_settings: ConnectionSettings):
187177
settings.setValue("page_size", connection_settings.page_size)
188178
settings.setValue("wfs_version", connection_settings.wfs_version.value)
189179
settings.setValue("auth_config", connection_settings.auth_config)
190-
settings.setValue(
191-
"geonode_version",
192-
(
193-
str(connection_settings.geonode_version)
194-
if connection_settings.geonode_version is not None
195-
else ""
196-
),
197-
)
198180

199181
def delete_connection(self, connection_id: uuid.UUID):
200182
if self.is_current_connection(connection_id):

src/qgis_geonode/gui/connection_dialog.py

Lines changed: 28 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from ..tasks import network_task
1717
from .. import apiclient, network, utils
1818
from ..apiclient.base import BaseGeonodeClient
19+
from ..apiclient import is_api_client_supported
1920
from ..conf import ConnectionSettings, WfsVersion, settings_manager, plugin_metadata
2021
from ..utils import tr
2122
from packaging import version as packaging_version
@@ -43,9 +44,6 @@ class ConnectionDialog(QtWidgets.QDialog, DialogUi):
4344
detected_capabilities_lw: QtWidgets.QListWidget
4445

4546
connection_id: uuid.UUID
46-
remote_geonode_version: typing.Optional[
47-
typing.Union[packaging_version.Version, str]
48-
]
4947
discovery_task: typing.Optional[network_task.NetworkRequestTask]
5048
geonode_client: BaseGeonodeClient = None
5149

@@ -69,7 +67,6 @@ def __init__(self, connection_settings: typing.Optional[ConnectionSettings] = No
6967
self._populate_wfs_version_combobox()
7068
if connection_settings is not None:
7169
self.connection_id = connection_settings.id
72-
self.remote_geonode_version = connection_settings.geonode_version
7370
self.name_le.setText(connection_settings.name)
7471
self.url_le.setText(connection_settings.base_url)
7572
self.authcfg_acs.setConfigId(connection_settings.auth_config)
@@ -78,15 +75,12 @@ def __init__(self, connection_settings: typing.Optional[ConnectionSettings] = No
7875
connection_settings.wfs_version
7976
)
8077
self.wfs_version_cb.setCurrentIndex(wfs_version_index)
81-
if self.remote_geonode_version == network.UNSUPPORTED_REMOTE:
82-
utils.show_message(
83-
self.bar,
84-
tr("Invalid configuration. Correct GeoNode URL and/or test again."),
85-
level=qgis.core.Qgis.Critical,
86-
)
78+
79+
self.detected_version_gb.setEnabled(False)
80+
self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
81+
8782
else:
8883
self.connection_id = uuid.uuid4()
89-
self.remote_geonode_version = None
9084
self.update_connection_details()
9185
# self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
9286
ok_signals = [
@@ -157,7 +151,6 @@ def get_connection_settings(self) -> ConnectionSettings:
157151
base_url=self.url_le.text().strip().rstrip("#/"),
158152
auth_config=self.authcfg_acs.configId(),
159153
page_size=self.page_size_sb.value(),
160-
geonode_version=self.remote_geonode_version,
161154
wfs_version=self.wfs_version_cb.currentData(),
162155
)
163156

@@ -166,38 +159,22 @@ def test_connection(self):
166159
widget.setEnabled(False)
167160

168161
current_settings = self.get_connection_settings()
169-
self.discovery_task = network_task.NetworkRequestTask(
170-
[
171-
network.RequestToPerform(
172-
QtCore.QUrl(f"{current_settings.base_url}/version.txt")
173-
)
174-
],
175-
network_task_timeout=current_settings.network_requests_timeout,
176-
authcfg=current_settings.auth_config,
177-
description="Connect to a GeoNode client",
178-
)
179-
self.discovery_task.task_done.connect(self.handle_discovery_test)
180-
utils.show_message(self.bar, tr("Connecting..."), add_loading_widget=True)
181-
qgis.core.QgsApplication.taskManager().addTask(self.discovery_task)
182162

183-
def handle_discovery_test(self, task_result: bool):
163+
# Replace old version.txt request with /api/v2/ check
164+
success = is_api_client_supported(current_settings.base_url)
165+
166+
self.handle_discovery_test(success)
167+
168+
def handle_discovery_test(self, is_supported: bool):
184169
self.enable_post_test_connection_buttons()
185-
geonode_version = network.handle_discovery_test(
186-
task_result, self.discovery_task
187-
)
188-
if geonode_version is not None:
189-
if apiclient.validate_version(geonode_version) == False:
190-
message = "This GeoNode version is not supported..."
191-
level = qgis.core.Qgis.Critical
192-
self.remote_geonode_version = network.UNSUPPORTED_REMOTE
193-
else:
194-
self.remote_geonode_version = geonode_version
195-
message = "Connection is valid"
196-
level = qgis.core.Qgis.Info
170+
171+
if is_supported:
172+
message = "Connection is valid"
173+
level = qgis.core.Qgis.Info
197174
else:
198-
message = "Connection is not valid"
175+
message = "This GeoNode instance does not support the API client."
199176
level = qgis.core.Qgis.Critical
200-
self.remote_geonode_version = network.UNSUPPORTED_REMOTE
177+
201178
utils.show_message(self.bar, message, level)
202179
self.update_connection_details()
203180

@@ -236,20 +213,24 @@ def handle_wfs_version_detection_test(self, task_result: bool):
236213
self.wfs_version_cb.setCurrentIndex(index)
237214

238215
def update_connection_details(self):
239-
invalid_version = (
240-
self.remote_geonode_version is None
241-
or self.remote_geonode_version == network.UNSUPPORTED_REMOTE
242-
)
216+
# Determine if API is reachable
217+
api_supported = is_api_client_supported(self.url_le.text().strip().rstrip("#/"))
218+
243219
self.detected_capabilities_lw.clear()
244220
self.detected_version_le.clear()
245-
if not invalid_version:
246-
# Enable the detected_version_db and OK button
221+
222+
if api_supported:
223+
# Enable the detected_version group box and OK button
247224
self.detected_version_gb.setEnabled(True)
248225
self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
249226

250227
current_settings = self.get_connection_settings()
251228
client: BaseGeonodeClient = apiclient.get_geonode_client(current_settings)
252-
self.detected_version_le.setText(str(current_settings.geonode_version))
229+
230+
# No version stored; just show "v2 API available"
231+
self.detected_version_le.setText("API v2 available")
232+
233+
# Show capabilities from client
253234
self.detected_capabilities_lw.insertItems(
254235
0, [cap.name for cap in client.capabilities]
255236
)

0 commit comments

Comments
 (0)