Skip to content

Commit 1be9c91

Browse files
author
Alessio Fabiani
authored
[Fixes GeoNode#8855] Plugged service type are ignored in the model validation (GeoNode#8863)
* [Fixes GeoNode#8855] Plugged service type are ignored in the model validation * - Make sure we are going to use the service GetCapa operation to fetch the FT attributes
1 parent 316f9c6 commit 1be9c91

20 files changed

+174
-120
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 3.2.12 on 2022-02-25 16:05
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('base', '0078_alter_resourcebase_metadata'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='resourcebase',
15+
name='alternate',
16+
field=models.CharField(blank=True, max_length=255, null=True),
17+
),
18+
]

geonode/base/models.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,7 @@ class ResourceBase(PolymorphicModel, PermissionLevelMixin, ItemBase):
797797
contacts = models.ManyToManyField(
798798
settings.AUTH_USER_MODEL,
799799
through='ContactRole')
800-
alternate = models.CharField(max_length=128, null=True, blank=True)
800+
alternate = models.CharField(max_length=255, null=True, blank=True)
801801
date = models.DateTimeField(
802802
_('date'),
803803
default=now,

geonode/geoserver/helpers.py

+19-8
Original file line numberDiff line numberDiff line change
@@ -979,7 +979,15 @@ def set_attributes_from_geoserver(layer, overwrite=False):
979979
then store in GeoNode database using Attribute model
980980
"""
981981
attribute_map = []
982-
server_url = ogc_server_settings.LOCATION if layer.subtype not in ['tileStore', 'remote'] else layer.remote_service.service_url
982+
if getattr(layer, 'remote_service') and layer.remote_service:
983+
server_url = layer.remote_service.service_url
984+
if layer.remote_service.operations.get('GetCapabilities', None) and layer.remote_service.operations.get('GetCapabilities').get('methods'):
985+
for _method in layer.remote_service.operations.get('GetCapabilities').get('methods'):
986+
if _method.get('type', '').upper() == 'GET':
987+
server_url = _method.get('url', server_url)
988+
break
989+
else:
990+
server_url = ogc_server_settings.LOCATION
983991
if layer.subtype in ['tileStore', 'remote'] and layer.remote_service.ptype == "gxp_arcrestsource":
984992
dft_url = f"{server_url}{(layer.alternate or layer.typename)}?f=json"
985993
try:
@@ -994,13 +1002,16 @@ def set_attributes_from_geoserver(layer, overwrite=False):
9941002
attribute_map = []
9951003
elif layer.subtype in {"vector", "tileStore", "remote", "wmsStore"}:
9961004
typename = layer.alternate if layer.alternate else layer.typename
997-
dft_url = re.sub(r"\/wms\/?$",
998-
"/",
999-
server_url) + "ows?" + urlencode({"service": "wfs",
1000-
"version": "1.0.0",
1001-
"request": "DescribeFeatureType",
1002-
"typename": typename,
1003-
})
1005+
dft_url_path = re.sub(r"\/wms\/?$", "/", server_url)
1006+
dft_query = urlencode(
1007+
{
1008+
"service": "wfs",
1009+
"version": "1.0.0",
1010+
"request": "DescribeFeatureType",
1011+
"typename": typename
1012+
}
1013+
)
1014+
dft_url = urljoin(dft_url_path, f"ows?{dft_query}")
10041015
try:
10051016
# The code below will fail if http_client cannot be imported or WFS not supported
10061017
req, body = http_client.get(dft_url, user=_user)

geonode/layers/enumerations.py

+1
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,5 @@
4545
'REST_IMG': 'gxp_arcrestsource',
4646
'HGL': 'gxp_hglsource',
4747
'GN_WMS': 'gxp_wmscsource',
48+
'SOS': 'gxp_sos',
4849
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Generated by Django 3.2.12 on 2022-02-25 16:05
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('layers', '0042_alter_dataset_options'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='dataset',
15+
name='name',
16+
field=models.CharField(max_length=255, verbose_name='Name'),
17+
),
18+
migrations.AlterField(
19+
model_name='dataset',
20+
name='ptype',
21+
field=models.CharField(default='gxp_wmscsource', max_length=255, verbose_name='P-Type'),
22+
),
23+
migrations.AlterField(
24+
model_name='dataset',
25+
name='store',
26+
field=models.CharField(max_length=255, verbose_name='Store'),
27+
),
28+
migrations.AlterField(
29+
model_name='dataset',
30+
name='typename',
31+
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Typename'),
32+
),
33+
migrations.AlterField(
34+
model_name='dataset',
35+
name='workspace',
36+
field=models.CharField(max_length=255, verbose_name='Workspace'),
37+
),
38+
]

geonode/layers/models.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,10 @@ class Dataset(ResourceBase):
128128

129129
# internal fields
130130
objects = DatasetManager()
131-
workspace = models.CharField(_('Workspace'), max_length=128)
132-
store = models.CharField(_('Store'), max_length=128)
133-
name = models.CharField(_('Name'), max_length=128)
134-
typename = models.CharField(_('Typename'), max_length=128, null=True, blank=True)
131+
workspace = models.CharField(_('Workspace'), max_length=255)
132+
store = models.CharField(_('Store'), max_length=255)
133+
name = models.CharField(_('Name'), max_length=255)
134+
typename = models.CharField(_('Typename'), max_length=255, null=True, blank=True)
135135
ows_url = models.URLField(
136136
_('ows URL'),
137137
null=True,
@@ -153,7 +153,7 @@ class Dataset(ResourceBase):
153153
_('P-Type'),
154154
null=False,
155155
blank=False,
156-
max_length=80,
156+
max_length=255,
157157
default="gxp_wmscsource")
158158

159159
default_style = models.ForeignKey(

geonode/resource/manager.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@
6969
resourcebase_post_save)
7070

7171
from ..base import enumerations
72-
from ..services.models import Service
7372
from ..base.models import ResourceBase
7473
from ..layers.metadata import parse_metadata
7574
from ..documents.models import Document, DocumentResourceLink
@@ -296,6 +295,8 @@ def delete(self, uuid: str, /, instance: ResourceBase = None) -> int:
296295
logger.exception(e)
297296

298297
try:
298+
from ..services.models import Service
299+
299300
if _resource.remote_typename and Service.objects.filter(name=_resource.remote_typename).exists():
300301
_service = Service.objects.filter(name=_resource.remote_typename).get()
301302
if _service.harvester:

geonode/resource/utils.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,6 @@
5656
from ..layers.utils import resolve_regions
5757
from ..layers.metadata import convert_keyword
5858

59-
from ..services.models import Service
60-
from ..harvesting.models import HarvestableResource
61-
6259
logger = logging.getLogger(__name__)
6360

6461
ogc_settings = OGC_Servers_Handler(settings.OGC_SERVER)['default']
@@ -245,6 +242,9 @@ def update_resource(instance: ResourceBase, xml_file: str = None, regions: list
245242
logger.error(f"{e} - {to_update}")
246243

247244
# Check for "remote services" availability
245+
from ..services.models import Service
246+
from ..harvesting.models import HarvestableResource
247+
248248
if HarvestableResource.objects.filter(geonode_resource__uuid=instance.uuid).exists():
249249
_h = HarvestableResource.objects.filter(geonode_resource__uuid=instance.uuid).get().harvester
250250
if Service.objects.filter(harvester=_h).exists():

geonode/services/enumerations.py

-16
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@
1717
#
1818
#########################################################################
1919

20-
from django.utils.translation import ugettext_lazy as _
21-
22-
2320
AUTO = "AUTO"
2421
OWS = "OWS"
2522
WMS = "WMS"
@@ -40,19 +37,6 @@
4037
LIVE = "X"
4138
OPENGEOPORTAL = "O"
4239

43-
SERVICE_TYPES = (
44-
# (AUTO, _('Auto-detect')),
45-
(OWS, _('Paired WMS/WFS/WCS')),
46-
(WMS, _('Web Map Service')),
47-
# (CSW, _('Catalogue Service')),
48-
(REST_MAP, _('ArcGIS REST MapServer')),
49-
(REST_IMG, _('ArcGIS REST ImageServer')),
50-
# (OGP, _('OpenGeoPortal')),
51-
# (HGL, _('Harvard Geospatial Library')),
52-
(GN_WMS, _('GeoNode (Web Map Service)')),
53-
# (GN_CSW, _('GeoNode (Catalogue Service)')),
54-
)
55-
5640
HARVESTER_TYPES = {
5741
'WMS': 'geonode.harvesting.harvesters.wms.OgcWmsHarvester',
5842
'GN_WMS': 'geonode.harvesting.harvesters.geonodeharvester.GeonodeUnifiedHarvesterWorker',

geonode/services/forms.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from . import enumerations
2828
from .models import Service
2929
from .serviceprocessors import get_service_handler
30-
from geonode.services.serviceprocessors.handler import get_available_service_type
30+
from geonode.services.serviceprocessors import get_available_service_types
3131

3232
logger = logging.getLogger(__name__)
3333

@@ -48,7 +48,7 @@ class CreateServiceForm(forms.Form):
4848
)
4949
type = forms.ChoiceField(
5050
label=_("Service Type"),
51-
choices=[(k, v["label"]) for k, v in get_available_service_type().items()], # from dictionary to tuple
51+
choices=[(k, v["label"]) for k, v in get_available_service_types().items()], # from dictionary to tuple
5252
initial='AUTO',
5353
)
5454

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 3.2.12 on 2022-02-25 16:05
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('services', '0053_alter_service_type'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='service',
15+
name='type',
16+
field=models.CharField(choices=[('WMS', 'Web Map Service'), ('GN_WMS', 'GeoNode (Web Map Service)'), ('REST_MAP', 'ArcGIS REST MapServer'), ('REST_IMG', 'ArcGIS REST ImageServer')], max_length=10),
17+
),
18+
]

geonode/services/models.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,21 @@
3030
from geonode.harvesting.models import Harvester
3131
from geonode.layers.enumerations import GXP_PTYPES
3232
from geonode.people.enumerations import ROLE_VALUES
33+
from geonode.services.serviceprocessors import get_available_service_types
3334

3435
from . import enumerations
3536

37+
service_type_as_tuple = [(k, v["label"]) for k, v in get_available_service_types().items()]
38+
3639
logger = logging.getLogger("geonode.services")
3740

3841

3942
class Service(ResourceBase):
4043
"""Service Class to represent remote Geo Web Services"""
4144

4245
type = models.CharField(
43-
max_length=100,
44-
choices=enumerations.SERVICE_TYPES
46+
max_length=10,
47+
choices=service_type_as_tuple
4548
)
4649
method = models.CharField(
4750
max_length=1,
@@ -126,7 +129,7 @@ def ptype(self):
126129
@property
127130
def service_type(self):
128131
# Return the gxp ptype that should be used to display layers
129-
return [x for x in enumerations.SERVICE_TYPES if x[0] == self.type][0][1]
132+
return [x for x in service_type_as_tuple if x[0] == self.type][0][1]
130133

131134
def get_absolute_url(self):
132135
return '/services/%i' % self.id

geonode/services/serviceprocessors/__init__.py

+44-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,48 @@
1616
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
#
1818
#########################################################################
19+
import logging
1920

20-
from .handler import get_service_handler # noqa
21+
from collections import OrderedDict
22+
from django.utils.translation import ugettext as _
23+
24+
from geonode.services import enumerations
25+
from geonode.services.utils import parse_services_types
26+
27+
logger = logging.getLogger(__name__)
28+
29+
30+
def get_available_service_types():
31+
# LGTM: Fixes - Module uses member of cyclically imported module, which can lead to failure at import time.
32+
from geonode.services.serviceprocessors.wms import GeoNodeServiceHandler, WmsServiceHandler
33+
from geonode.services.serviceprocessors.arcgis import ArcImageServiceHandler, ArcMapServiceHandler
34+
35+
default = OrderedDict({
36+
enumerations.WMS: {"OWS": True, "handler": WmsServiceHandler, "label": _('Web Map Service')},
37+
enumerations.GN_WMS: {"OWS": True, "handler": GeoNodeServiceHandler, "label": _('GeoNode (Web Map Service)')},
38+
# enumerations.WFS: {"OWS": True, "handler": ServiceHandlerBase, "label": _('Paired WMS/WFS/WCS'},
39+
# enumerations.TMS: {"OWS": False, "handler": ServiceHandlerBase, "label": _('Paired WMS/WFS/WCS'},
40+
enumerations.REST_MAP: {"OWS": False, "handler": ArcMapServiceHandler, "label": _('ArcGIS REST MapServer')},
41+
enumerations.REST_IMG: {"OWS": False, "handler": ArcImageServiceHandler, "label": _('ArcGIS REST ImageServer')},
42+
# enumerations.CSW: {"OWS": False, "handler": ServiceHandlerBase, "label": _('Catalogue Service')},
43+
# enumerations.OGP: {"OWS": True, "handler": ServiceHandlerBase, "label": _('OpenGeoPortal')}, # TODO: verify this
44+
# enumerations.HGL: {"OWS": False, "handler": ServiceHandlerBase, "label": _('Harvard Geospatial Library')}, # TODO: verify this
45+
})
46+
47+
return OrderedDict({**default, **parse_services_types()})
48+
49+
50+
def get_service_handler(base_url, service_type=enumerations.AUTO, service_id=None):
51+
"""Return the appropriate remote service handler for the input URL.
52+
If the service type is not explicitly passed in it will be guessed from
53+
"""
54+
handlers = get_available_service_types()
55+
56+
handler = handlers.get(service_type, {}).get("handler")
57+
try:
58+
service = handler(base_url, service_id)
59+
except Exception:
60+
logger.exception(
61+
msg=f"Could not parse service {base_url}")
62+
raise
63+
return service

geonode/services/serviceprocessors/arcgis.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def probe(self):
9999
def create_cascaded_store(self, service):
100100
return None
101101

102-
def create_geonode_service(self, owner):
102+
def create_geonode_service(self, owner, parent=None):
103103
"""Create a new geonode.service.models.Service instance
104104
105105
:arg owner: The user who will own the service instance

geonode/services/serviceprocessors/base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def probe(self):
8585
def get_harvester_configuration_options(self):
8686
return {}
8787

88-
def create_geonode_service(self, owner):
88+
def create_geonode_service(self, owner, parent=None):
8989
"""Create a new geonode.service.models.Service instance
9090
Saving the service instance in the database is not a concern of this
9191
method, it only deals with creating the instance.

0 commit comments

Comments
 (0)