Skip to content

Commit 1500dc6

Browse files
committed
Merge branch 'master' of https://github.com/geopython/pygeoapi into feat-reload-official
2 parents 71a3986 + 9d60950 commit 1500dc6

31 files changed

+381
-140
lines changed

.github/workflows/main.yml

+1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ jobs:
128128
pytest tests/test_esri_provider.py
129129
pytest tests/test_filesystem_provider.py
130130
pytest tests/test_geojson_provider.py
131+
pytest tests/test_linked_data.py
131132
pytest tests/test_mongo_provider.py
132133
pytest tests/test_ogr_csv_provider.py
133134
pytest tests/test_ogr_esrijson_provider.py

docs/source/configuration.rst

+5-10
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,6 @@ default.
197197
- observations
198198
- monitoring
199199
linked-data: # linked data configuration (see Linked Data section)
200-
item_template: tests/data/base.jsonld
201200
context:
202201
- datetime: https://schema.org/DateTime
203202
- vocab: https://example.com/vocab#
@@ -646,23 +645,19 @@ This relationship can further be maintained in the JSON-LD structured data using
646645
ssn: "http://www.w3.org/ns/ssn/"
647646
Datastream: sosa:isMemberOf
648647
649-
Sometimes, the JSON-LD desired for an individual feature in a collection is more complicated than can be achieved by
650-
aliasing properties using a context. In this case, it is possible to specify a Jinja2 template. When ``item_template``
651-
is defined for a feature collection, the json-ld prepared by pygeoapi will be used to render the Jinja2 template
652-
specified by the path. The path specified can be absolute or relative to pygeoapi's template folder. For even more
653-
deployment flexibility, the path can be specified with string interpolation of environment variables.
648+
Sometimes, the JSON-LD desired for an individual feature in a collection is more complicated than can
649+
be achieved by aliasing properties using a context. In this case, it is possible to implement a custom
650+
Jinja2 template. GeoJSON-LD is rendered using the Jinja2 templates defined in ``collections/items/item.jsonld``
651+
and ``collections/items/index.jsonld``. A pygeoapi collection requiring custom GeoJSON-LD can overwrite these
652+
templates using dataset level templating. To learn more about Jinja2 templates, see :ref:`html-templating`.
654653

655654

656655
.. code-block:: yaml
657656
658657
linked-data:
659-
item_template: tests/data/base.jsonld
660658
context:
661659
- datetime: https://schema.org/DateTime
662660
663-
.. note::
664-
The template ``tests/data/base.jsonld`` renders the unmodified JSON-LD. For more information on the capacities
665-
of Jinja2 templates, see :ref:`html-templating`.
666661
667662
Validating the configuration
668663
----------------------------

docs/source/installation.rst

+4-3
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ Docker
5959
Using DockerHub
6060
^^^^^^^^^^^^^^^
6161

62-
`Docker image`_
62+
`DockerHub image`_
6363

6464
.. code-block:: bash
6565
@@ -68,7 +68,7 @@ Using DockerHub
6868
Using GitHub Container Registry
6969
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7070

71-
`Docker image`_
71+
`GCHR image`_
7272

7373
.. code-block:: bash
7474
@@ -148,5 +148,6 @@ Congratulations! Whichever of the abovementioned methods you chose, you have su
148148
onto your system.
149149

150150

151-
.. _`Docker image`: https://github.com/geopython/pygeoapi/pkgs/container/pygeoapi
151+
.. _`DockerHub image`: https://hub.docker.com/r/geopython/pygeoapi
152+
.. _`GCHR image`: https://github.com/geopython/pygeoapi/pkgs/container/pygeoapi
152153
.. _`Dockerfile`: https://github.com/geopython/pygeoapi/blob/master/Dockerfile

pygeoapi/admin.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ def get_config_(
176176

177177
if request.format == F_HTML:
178178
content = render_j2_template(
179-
admin.config, 'admin/index.html', cfg, request.locale
179+
admin.tpl_config, admin.config['server']['templates'],
180+
'admin/index.html', cfg, request.locale
180181
)
181182
else:
182183
content = to_json(cfg, admin.pretty_print)
@@ -316,10 +317,8 @@ def get_resources(
316317

317318
if request.format == F_HTML:
318319
content = render_j2_template(
319-
admin.config,
320-
'admin/index.html',
321-
cfg['resources'],
322-
request.locale,
320+
admin.tpl_config, admin.config['server']['templates'],
321+
'admin/index.html', cfg['resources'], request.locale
323322
)
324323
else:
325324
content = to_json(cfg['resources'], admin.pretty_print)
@@ -422,7 +421,8 @@ def get_resource(
422421

423422
if request.format == F_HTML:
424423
content = render_j2_template(
425-
admin.config, 'admin/index.html', resource, request.locale
424+
admin.tpl_config, admin.config['server']['templates'],
425+
'admin/index.html', resource, request.locale
426426
)
427427
else:
428428
content = to_json(resource, admin.pretty_print)

pygeoapi/api/__init__.py

+38-33
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@
6464

6565
from pygeoapi.util import (
6666
CrsTransformSpec, TEMPLATES, UrlPrefetcher, dategetter,
67-
filter_dict_by_key_value, get_api_rules, get_base_url,
68-
get_provider_by_type, get_provider_default, get_typed_value,
67+
filter_dict_by_key_value, filter_providers_by_type, get_api_rules,
68+
get_base_url, get_provider_by_type, get_provider_default, get_typed_value,
6969
get_crs_from_uri, get_supported_crs_list, render_j2_template, to_json
7070
)
7171

@@ -686,7 +686,8 @@ def get_exception(self, status, headers, format_, code,
686686
if format_ == F_HTML:
687687
headers['Content-Type'] = FORMAT_TYPES[F_HTML]
688688
content = render_j2_template(
689-
self.config, 'exception.html', exception, SYSTEM_LOCALE)
689+
self.tpl_config, self.config['server']['templates'],
690+
'exception.html', exception, SYSTEM_LOCALE)
690691
else:
691692
content = to_json(exception, self.pretty_print)
692693

@@ -710,12 +711,13 @@ def get_format_exception(self, request) -> Tuple[dict, int, str]:
710711
HTTPStatus.BAD_REQUEST, headers,
711712
request.format, 'InvalidParameterValue', msg)
712713

713-
def get_collections_url(self):
714+
def get_collections_url(self) -> str:
714715
return f"{self.base_url}/collections"
715716

716-
def set_dataset_templates(self, dataset):
717-
if 'templates' in self.config['resources'][dataset]:
718-
self.tpl_config['server']['templates'] = self.config['resources'][dataset]['templates'] # noqa
717+
def get_dataset_templates(self, dataset) -> dict:
718+
templates = self.config['resources'][dataset].get('templates')
719+
720+
return templates or self.tpl_config['server']['templates']
719721

720722
@staticmethod
721723
def _create_crs_transform_spec(
@@ -904,24 +906,23 @@ def landing_page(api: API,
904906
headers = request.get_response_headers(**api.api_headers)
905907
if request.format == F_HTML: # render
906908

907-
fcm['processes'] = False
908-
fcm['stac'] = False
909-
fcm['collection'] = False
910-
911-
if filter_dict_by_key_value(api.config['resources'],
912-
'type', 'process'):
913-
fcm['processes'] = True
909+
for resource_type in ['collection', 'process', 'stac-collection']:
910+
fcm[resource_type] = False
914911

915-
if filter_dict_by_key_value(api.config['resources'],
916-
'type', 'stac-collection'):
917-
fcm['stac'] = True
912+
found = filter_dict_by_key_value(api.config['resources'],
913+
'type', resource_type)
914+
if found:
915+
fcm[resource_type] = True
916+
if resource_type == 'collection': # check for tiles
917+
for key, value in found.items():
918+
if filter_providers_by_type(value['providers'],
919+
'tile'):
920+
fcm['tile'] = True
918921

919-
if filter_dict_by_key_value(api.config['resources'],
920-
'type', 'collection'):
921-
fcm['collection'] = True
922+
content = render_j2_template(
923+
api.tpl_config, api.config['server']['templates'],
924+
'landing_page.html', fcm, request.locale)
922925

923-
content = render_j2_template(api.tpl_config, 'landing_page.html',
924-
fcm, request.locale)
925926
return headers, HTTPStatus.OK, content
926927

927928
if request.format == F_JSONLD:
@@ -951,8 +952,10 @@ def openapi_(api: API, request: APIRequest) -> Tuple[dict, int, str]:
951952
data = {
952953
'openapi-document-path': path
953954
}
954-
content = render_j2_template(api.tpl_config, template, data,
955-
request.locale)
955+
content = render_j2_template(
956+
api.tpl_config, api.config['server']['templates'], template, data,
957+
request.locale)
958+
956959
return headers, HTTPStatus.OK, content
957960

958961
headers['Content-Type'] = 'application/vnd.oai.openapi+json;version=3.0' # noqa
@@ -999,8 +1002,10 @@ def conformance(api, request: APIRequest) -> Tuple[dict, int, str]:
9991002

10001003
headers = request.get_response_headers(**api.api_headers)
10011004
if request.format == F_HTML: # render
1002-
content = render_j2_template(api.tpl_config, 'conformance.html',
1003-
conformance, request.locale)
1005+
content = render_j2_template(
1006+
api.tpl_config, api.config['server']['templates'],
1007+
'conformance.html', conformance, request.locale)
1008+
10041009
return headers, HTTPStatus.OK, content
10051010

10061011
return headers, HTTPStatus.OK, to_json(conformance, api.pretty_print)
@@ -1428,14 +1433,14 @@ def describe_collections(api: API, request: APIRequest,
14281433
if request.format == F_HTML: # render
14291434
fcm['collections_path'] = api.get_collections_url()
14301435
if dataset is not None:
1431-
api.set_dataset_templates(dataset)
1432-
content = render_j2_template(api.tpl_config,
1436+
tpl_config = api.get_dataset_templates(dataset)
1437+
content = render_j2_template(api.tpl_config, tpl_config,
14331438
'collections/collection.html',
14341439
fcm, request.locale)
14351440
else:
1436-
content = render_j2_template(api.tpl_config,
1437-
'collections/index.html',
1438-
fcm, request.locale)
1441+
content = render_j2_template(
1442+
api.tpl_config, api.config['server']['templates'],
1443+
'collections/index.html', fcm, request.locale)
14391444

14401445
return headers, HTTPStatus.OK, content
14411446

@@ -1520,14 +1525,14 @@ def get_collection_schema(api: API, request: Union[APIRequest, Any],
15201525
schema['properties'][k]['x-ogc-role'] = 'primary-instant'
15211526

15221527
if request.format == F_HTML: # render
1523-
api.set_dataset_templates(dataset)
1528+
tpl_config = api.get_dataset_templates(dataset)
15241529
schema['title'] = l10n.translate(
15251530
api.config['resources'][dataset]['title'], request.locale)
15261531

15271532
schema['collections_path'] = api.get_collections_url()
15281533
schema['dataset_path'] = f'{api.get_collections_url()}/{dataset}'
15291534

1530-
content = render_j2_template(api.tpl_config,
1535+
content = render_j2_template(api.tpl_config, tpl_config,
15311536
'collections/schema.html',
15321537
schema, request.locale)
15331538

pygeoapi/api/environmental_data_retrieval.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def get_collection_edr_instances(api: API, request: APIRequest, dataset,
173173
}])
174174

175175
if request.format == F_HTML: # render
176-
api.set_dataset_templates(dataset)
176+
tpl_config = api.get_dataset_templates(dataset)
177177

178178
serialized_query_params = ''
179179
for k, v in request.params.items():
@@ -224,8 +224,8 @@ def get_collection_edr_instances(api: API, request: APIRequest, dataset,
224224
'href': f'{uri}?f={F_JSONLD}{serialized_query_params}'
225225
}]
226226

227-
content = render_j2_template(api.tpl_config, template, data,
228-
api.default_locale)
227+
content = render_j2_template(api.tpl_config, tpl_config, template,
228+
data, api.default_locale)
229229
else:
230230
content = to_json(data, api.pretty_print)
231231

@@ -381,7 +381,7 @@ def get_collection_edr_query(api: API, request: APIRequest,
381381
err.ogc_exception_code, err.message)
382382

383383
if request.format == F_HTML: # render
384-
api.set_dataset_templates(dataset)
384+
tpl_config = api.get_dataset_templates(dataset)
385385

386386
uri = f'{api.get_collections_url()}/{dataset}/{query_type}'
387387
serialized_query_params = ''
@@ -413,7 +413,7 @@ def get_collection_edr_query(api: API, request: APIRequest,
413413
'href': f'{uri}?f={F_JSONLD}{serialized_query_params}'
414414
}]
415415

416-
content = render_j2_template(api.tpl_config,
416+
content = render_j2_template(api.tpl_config, tpl_config,
417417
'collections/edr/query.html', data,
418418
api.default_locale)
419419
else:

pygeoapi/api/itemtypes.py

+10-6
Original file line numberDiff line numberDiff line change
@@ -178,15 +178,15 @@ def get_collection_queryables(api: API, request: Union[APIRequest, Any],
178178
queryables['properties'][k]['x-ogc-role'] = 'primary-instant' # noqa
179179

180180
if request.format == F_HTML: # render
181-
api.set_dataset_templates(dataset)
181+
tpl_config = api.get_dataset_templates(dataset)
182182

183183
queryables['title'] = l10n.translate(
184184
api.config['resources'][dataset]['title'], request.locale)
185185

186186
queryables['collections_path'] = api.get_collections_url()
187187
queryables['dataset_path'] = f'{api.get_collections_url()}/{dataset}'
188188

189-
content = render_j2_template(api.tpl_config,
189+
content = render_j2_template(api.tpl_config, tpl_config,
190190
'collections/queryables.html',
191191
queryables, request.locale)
192192

@@ -593,7 +593,7 @@ def get_collection_items(
593593
l10n.set_response_language(headers, prv_locale, request.locale)
594594

595595
if request.format == F_HTML: # render
596-
api.set_dataset_templates(dataset)
596+
tpl_config = api.get_dataset_templates(dataset)
597597
# For constructing proper URIs to items
598598

599599
content['items_path'] = uri
@@ -610,7 +610,7 @@ def get_collection_items(
610610
request.locale)
611611
# If title exists, use it as id in html templates
612612
content['id_field'] = content['title_field']
613-
content = render_j2_template(api.tpl_config,
613+
content = render_j2_template(api.tpl_config, tpl_config,
614614
'collections/items/index.html',
615615
content, request.locale)
616616
return headers, HTTPStatus.OK, content
@@ -650,6 +650,8 @@ def get_collection_items(
650650
api, content, dataset, id_field=(p.uri_field or 'id')
651651
)
652652

653+
return headers, HTTPStatus.OK, content
654+
653655
return headers, HTTPStatus.OK, to_json(content, api.pretty_print)
654656

655657

@@ -913,7 +915,7 @@ def get_collection_item(api: API, request: APIRequest,
913915
l10n.set_response_language(headers, prv_locale, request.locale)
914916

915917
if request.format == F_HTML: # render
916-
api.set_dataset_templates(dataset)
918+
tpl_config = api.get_dataset_templates(dataset)
917919
content['title'] = l10n.translate(collections[dataset]['title'],
918920
request.locale)
919921
content['id_field'] = p.id_field
@@ -924,7 +926,7 @@ def get_collection_item(api: API, request: APIRequest,
924926
request.locale)
925927
content['collections_path'] = api.get_collections_url()
926928

927-
content = render_j2_template(api.tpl_config,
929+
content = render_j2_template(api.tpl_config, tpl_config,
928930
'collections/items/item.html',
929931
content, request.locale)
930932
return headers, HTTPStatus.OK, content
@@ -934,6 +936,8 @@ def get_collection_item(api: API, request: APIRequest,
934936
api, content, dataset, uri, (p.uri_field or 'id')
935937
)
936938

939+
return headers, HTTPStatus.OK, content
940+
937941
return headers, HTTPStatus.OK, to_json(content, api.pretty_print)
938942

939943

pygeoapi/api/processes.py

+11-9
Original file line numberDiff line numberDiff line change
@@ -211,14 +211,14 @@ def describe_processes(api: API, request: APIRequest,
211211

212212
if request.format == F_HTML: # render
213213
if process is not None:
214-
api.set_dataset_templates(process)
215-
response = render_j2_template(api.tpl_config,
214+
tpl_config = api.get_dataset_templates(process)
215+
response = render_j2_template(api.tpl_config, tpl_config,
216216
'processes/process.html',
217217
response, request.locale)
218218
else:
219-
response = render_j2_template(api.tpl_config,
220-
'processes/index.html', response,
221-
request.locale)
219+
response = render_j2_template(
220+
api.tpl_config, api.config['server']['templates'],
221+
'processes/index.html', response, request.locale)
222222

223223
return headers, HTTPStatus.OK, response
224224

@@ -394,8 +394,10 @@ def get_jobs(api: API, request: APIRequest,
394394
'offset': offset,
395395
'now': datetime.now(timezone.utc).strftime(DATETIME_FORMAT)
396396
}
397-
response = render_j2_template(api.tpl_config, j2_template, data,
398-
request.locale)
397+
response = render_j2_template(
398+
api.tpl_config, api.config['server']['templates'], j2_template,
399+
data, request.locale)
400+
399401
return headers, HTTPStatus.OK, response
400402

401403
return headers, HTTPStatus.OK, to_json(serialized_jobs,
@@ -586,8 +588,8 @@ def get_job_result(api: API, request: APIRequest,
586588
'result': job_output
587589
}
588590
content = render_j2_template(
589-
api.config, 'jobs/results/index.html',
590-
data, request.locale)
591+
api.config, api.config['server']['templates'],
592+
'jobs/results/index.html', data, request.locale)
591593

592594
return headers, HTTPStatus.OK, content
593595

0 commit comments

Comments
 (0)