Skip to content

Commit 4794363

Browse files
authored
Merge pull request #2312 from geopython/issue-2304
OAProc: fix binary outputs and non-JSON output (#2304) (#2311)
2 parents 08031d4 + de09bd3 commit 4794363

7 files changed

Lines changed: 65 additions & 29 deletions

File tree

pygeoapi/api/itemtypes.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,6 @@ def get_collection_items(
615615
if offset > 0:
616616
prev_link = True
617617

618-
print(request.format)
619618
if prev_link:
620619
prev = max(0, offset - limit)
621620
url = f'{uri}?offset={prev}{serialized_query_params}'

pygeoapi/api/processes.py

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -327,27 +327,12 @@ def get_jobs(api: API, request: APIRequest,
327327
job_result_url = f"{api.base_url}/jobs/{job_['identifier']}/results" # noqa
328328

329329
job2['links'] = [{
330-
'href': f'{job_result_url}?f={F_HTML}',
330+
'href': job_result_url,
331331
'rel': 'http://www.opengis.net/def/rel/ogc/1.0/results',
332-
'type': FORMAT_TYPES[F_HTML],
333-
'title': l10n.translate(f'Results of job as HTML', request.locale), # noqa
334-
}, {
335-
'href': f'{job_result_url}?f={F_JSON}',
336-
'rel': 'http://www.opengis.net/def/rel/ogc/1.0/results',
337-
'type': FORMAT_TYPES[F_JSON],
338-
'title': l10n.translate(f'Results of job as JSON', request.locale), # noqa
332+
'type': job_['mimetype'],
333+
'title': f"Results of job {job_id} as {job_['mimetype']}"
339334
}]
340335

341-
if job_['mimetype'] not in (FORMAT_TYPES[F_JSON],
342-
FORMAT_TYPES[F_HTML]):
343-
344-
job2['links'].append({
345-
'href': job_result_url,
346-
'rel': 'http://www.opengis.net/def/rel/ogc/1.0/results', # noqa
347-
'type': job_['mimetype'],
348-
'title': f"Results of job {job_id} as {job_['mimetype']}" # noqa
349-
})
350-
351336
serialized_jobs['jobs'].append(job2)
352337

353338
serialized_query_params = ''
@@ -528,7 +513,10 @@ def execute_process(api: API, request: APIRequest,
528513
pretty_print_ = False
529514
response2 = to_json(response, pretty_print_)
530515
else:
516+
pretty_print_ = False
531517
response2 = response
518+
if isinstance(response, (list, dict)):
519+
response2 = to_json(response, pretty_print_)
532520

533521
if (headers.get('Preference-Applied', '') == RequestedProcessExecutionMode.respond_async.value): # noqa
534522
LOGGER.debug('Asynchronous mode detected, returning statusInfo')

pygeoapi/process/hello_world.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Authors: Tom Kralidis <tomkralidis@gmail.com>
44
# Francesco Martinelli <francesco.martinelli@ingv.it>
55
#
6-
# Copyright (c) 2022 Tom Kralidis
6+
# Copyright (c) 2026 Tom Kralidis
77
# Copyright (c) 2024 Francesco Martinelli
88
#
99
# Permission is hereby granted, free of charge, to any person
@@ -29,6 +29,7 @@
2929
#
3030
# =================================================================
3131

32+
import json
3233
import logging
3334

3435
from pygeoapi.process.base import BaseProcessor, ProcessorExecuteError
@@ -82,6 +83,28 @@
8283
'minOccurs': 0,
8384
'maxOccurs': 1,
8485
'keywords': ['message']
86+
},
87+
'as_bytes': {
88+
'title': 'As bytes',
89+
'description': 'Whether to force return as bytes',
90+
'schema': {
91+
'type': 'bool',
92+
'default': False
93+
},
94+
'minOccurs': 0,
95+
'maxOccurs': 1,
96+
'keywords': ['as_bytes']
97+
},
98+
'media_type': {
99+
'title': 'Media type',
100+
'description': 'Force a specific media type',
101+
'schema': {
102+
'type': 'string',
103+
'default': 'application/json'
104+
},
105+
'minOccurs': 0,
106+
'maxOccurs': 1,
107+
'keywords': ['media_type']
85108
}
86109
},
87110
'outputs': {
@@ -120,7 +143,7 @@ def __init__(self, processor_def):
120143
self.supports_outputs = True
121144

122145
def execute(self, data, outputs=None):
123-
mimetype = 'application/json'
146+
mimetype = data.get('media_type', 'application/json')
124147
name = data.get('name')
125148

126149
if name is None:
@@ -136,6 +159,9 @@ def execute(self, data, outputs=None):
136159
'value': value
137160
}
138161

162+
if data.get('as_bytes', False):
163+
json.dumps(produced_outputs).encode('utf-8')
164+
139165
return mimetype, produced_outputs
140166

141167
def __repr__(self):

pygeoapi/process/manager/base.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Ricardo Garcia Silva <ricardo.garcia.silva@geobeyond.it>
55
# Francesco Martinelli <francesco.martinelli@ingv.it>
66
#
7-
# Copyright (c) 2024 Tom Kralidis
7+
# Copyright (c) 2026 Tom Kralidis
88
# (c) 2023 Ricardo Garcia Silva
99
# (c) 2026 Francesco Martinelli
1010
#
@@ -277,7 +277,8 @@ def _execute_handler_sync(self, p: BaseProcessor, job_id: str,
277277
current_status = JobStatus.running
278278
jfmt, outputs = p.execute(data_dict, **extra_execute_parameters)
279279

280-
if isinstance(outputs, bytes):
280+
if isinstance(outputs, bytes) and outputs.isascii():
281+
LOGGER.debug('output is ASCII; decoding utf-8')
281282
outputs = outputs.decode('utf-8')
282283

283284
if requested_response == RequestedResponse.document.value:

tests/api/test_api.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,6 @@ def test_describe_collections(config, api_):
622622
collections = json.loads(response)
623623

624624
assert len(collections) == 2
625-
print(json.dumps(collections['collections']))
626625
assert len(collections['collections']) == 10
627626
assert len(collections['links']) == 3
628627

tests/api/test_processes.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def test_describe_processes(config, api_):
9595
assert process['title'] == 'Hello World'
9696
assert len(process['keywords']) == 3
9797
assert len(process['links']) == 6
98-
assert len(process['inputs']) == 2
98+
assert len(process['inputs']) == 4
9999
assert len(process['outputs']) == 1
100100
assert len(process['outputTransmission']) == 1
101101
assert len(process['jobControlOptions']) == 2
@@ -242,6 +242,18 @@ def test_execute_process(config, api_):
242242
'name': 'Test document'
243243
}
244244
}
245+
req_body_10 = {
246+
'inputs': {
247+
'name': 'Test document as bytes response',
248+
'as_bytes': True
249+
}
250+
}
251+
req_body_11 = {
252+
'inputs': {
253+
'name': 'Test document as text/plain media type',
254+
'media_type': 'text/plain'
255+
}
256+
}
245257

246258
cleanup_jobs = set()
247259

@@ -410,6 +422,19 @@ def test_execute_process(config, api_):
410422
response2 = '{"id":"echo","value":"Hello Test document!"}'
411423
assert response == response2
412424

425+
req = mock_api_request(data=req_body_10)
426+
rsp_headers, code, response = execute_process(api_, req, 'hello-world')
427+
428+
response2 = '{"id":"echo","value":"Hello Test document as bytes response!"}' # noqa
429+
assert response == response2
430+
431+
req = mock_api_request(data=req_body_11)
432+
rsp_headers, code, response = execute_process(api_, req, 'hello-world')
433+
434+
assert rsp_headers['Content-Type'] == 'text/plain'
435+
response2 = '{"id":"echo","value":"Hello Test document as text/plain media type!"}' # noqa
436+
assert response == response2
437+
413438
# Cleanup
414439
time.sleep(2) # Allow time for any outstanding async jobs
415440
for _, job_id in cleanup_jobs:

tests/other/test_ogr_capabilities.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# =================================================================
22
#
33
# Authors: Just van den Broecke <justb4@gmail.com>
4+
# Tom Kralidis <tomkralidis@gmail.com>
45
#
56
# Copyright (c) 2023 Just van den Broecke
7+
# Copyright (c) 2026 Tom Kralidis
68
#
79
# Permission is hereby granted, free of charge, to any person
810
# obtaining a copy of this software and associated documentation
@@ -50,7 +52,6 @@ def get_axis_order(coords):
5052
def test_transforms():
5153
version_num = int(gdal.VersionInfo('VERSION_NUM'))
5254
assert version_num > 3000000, f'GDAL version={version_num} must be > 3.0.0'
53-
print(f'GDAL Version num = {version_num}')
5455

5556
pyproj.show_versions()
5657
FORCE_LON_LAT = osr.OAMS_TRADITIONAL_GIS_ORDER
@@ -68,7 +69,6 @@ def test_transforms():
6869
}
6970

7071
for crs in CRS_DICT:
71-
print(f'Testing CRS={crs}')
7272
crs_entry = CRS_DICT[crs]
7373
source = get_spatial_ref(28992, AUTH_COMPLIANT)
7474
target = get_spatial_ref(crs_entry['epsg'], crs_entry['mapping'])
@@ -85,7 +85,6 @@ def test_transforms():
8585
axis_order = get_axis_order(result)
8686

8787
# Axis order should match that of CRS
88-
print(f'Transform result={result} Axis order={axis_order}')
8988
crs_axis_order = crs_entry['order']
9089
assert axis_order == crs_axis_order, f'Axis order for {crs} after Transform should be {crs_axis_order} result={result}' # noqa
9190

@@ -106,7 +105,6 @@ def test_transforms():
106105
# Determine Axis order after ExportToJson
107106
coords = json_feature['geometry']['coordinates']
108107
axis_order = get_axis_order(coords)
109-
print(f'ExportToJson result={coords} Axis order={axis_order}')
110108
assert axis_order == crs_axis_order, f'Axis order for {crs} after ExportToJson should be {crs_axis_order} coords={coords}' # noqa
111109

112110

0 commit comments

Comments
 (0)