Skip to content

Commit fc0158c

Browse files
authored
Merge pull request #35 from actinia-org/bbox-validator-fix
Flatten bbox and reorder execution validation
2 parents c1008a0 + 0c110b0 commit fc0158c

File tree

4 files changed

+70
-49
lines changed

4 files changed

+70
-49
lines changed

src/actinia_ogc_api_processes_plugin/core/process_description.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,12 @@ def update_resp(resp_json: dict) -> dict:
5656
),
5757
"name": "bounding_box",
5858
"optional": True,
59-
"schema": {"type": "bbox"},
59+
"schema": {
60+
"type": "array",
61+
"items": {"type": "number"},
62+
"minItems": 4,
63+
"maxItems": 6,
64+
},
6065
}
6166
resp_json["parameters"].append(bounding_box_input)
6267

src/actinia_ogc_api_processes_plugin/core/process_execution.py

Lines changed: 58 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,25 @@ def _invalid_inputs(module_info: dict, inputs: dict):
213213
"""
214214
params = module_info.get("parameters", [])
215215
params += module_info.get("returns", [])
216-
params.append({"name": "bounding_box", "schema": {"type": "bbox"}})
217-
params.append({"name": "project", "schema": {"type": "string"}})
216+
params.append(
217+
{
218+
"name": "bounding_box",
219+
"schema": {
220+
"type": "array",
221+
"items": {"type": "number"},
222+
"minItems": 4,
223+
"maxItems": 6,
224+
},
225+
"optional": True,
226+
},
227+
)
228+
params.append(
229+
{
230+
"name": "project",
231+
"schema": {"type": "string"},
232+
"optional": True,
233+
},
234+
)
218235
param_map = {
219236
p.get("name"): p
220237
for p in params
@@ -267,30 +284,30 @@ def _invalid_inputs(module_info: dict, inputs: dict):
267284
elif expected == "array":
268285
if not isinstance(val, list):
269286
invalid.append(key)
270-
elif expected == "bbox":
271-
# ruff: noqa: PLR0916
287+
# ruff: noqa: SIM102
288+
# Use a single `if` statement instead of nested `if` statements
289+
# keep for readability
272290
if (
273-
not isinstance(val, dict)
274-
or "bbox" not in val
275-
or not isinstance(val["bbox"], list)
276-
or (len(val["bbox"]) != 4 and len(val["bbox"]) != 6)
277-
or not all(
278-
isinstance(coord, (int, float)) for coord in val["bbox"]
279-
)
291+
schema.get("items", {}).get("type") == "number"
292+
and schema.get("minItems") == 4
293+
and schema.get("maxItems") == 6
280294
):
281-
invalid.append(key)
282-
msg_append += (
283-
"Check if 'bounding_box' is dict with key 'bbox'."
284-
"'bbox' should be a list of 4 or 6 numbers."
285-
)
295+
# Case for bbox type
296+
if (len(val) != 4 and len(val) != 6) or not all(
297+
isinstance(item, (int, float)) for item in val
298+
):
299+
invalid.append(key)
300+
msg_append += (
301+
"Check if 'bounding_box' is list of 4 or 6 numbers."
302+
)
286303
else:
287304
# Unknown/unsupported schema type: be permissive and accept
288305
continue
289306

290307
if key in invalid:
291308
msg += (
292309
f"Input parameter '{key}' should be type {expected},"
293-
f" but got {type(val).__name__}. {msg_append}"
310+
f" got {type(val).__name__}. {msg_append}"
294311
)
295312

296313
return invalid, msg
@@ -401,8 +418,28 @@ def post_process_execution(
401418
if resp.status_code != 200:
402419
return resp
403420

421+
module_info = resp.json()
422+
423+
if "grass-module" in module_info["categories"]:
424+
# get GRASS processing type
425+
process_type = GRASS_MODULE_TYPE[process_id.split(".", 1)[0]]
426+
# check if module has input/map parameter
427+
has_input = any(
428+
param.get("name") in {"input", "map"}
429+
for param in module_info.get("parameters", [])
430+
)
431+
if not has_input or process_type not in {"raster", "vector"}:
432+
msg = f"Process execution of <{process_id}> not supported."
433+
res = jsonify(
434+
SimpleStatusCodeResponseModel(
435+
status=400,
436+
message=msg,
437+
),
438+
)
439+
return make_response(res, 400)
440+
404441
invalid_inputs, detail_msg = _invalid_inputs(
405-
resp.json(),
442+
module_info,
406443
postbody.get("inputs", {}),
407444
)
408445
if invalid_inputs:
@@ -419,7 +456,7 @@ def post_process_execution(
419456
return make_response(res, 400)
420457

421458
missing_inputs = _missing_inputs(
422-
resp.json(),
459+
module_info,
423460
postbody.get("inputs", {}),
424461
)
425462
if missing_inputs:
@@ -449,25 +486,7 @@ def post_process_execution(
449486
pc = _transform_to_actinia_process_chain(process_id, postbody)
450487

451488
# adjust pc if process is grass module
452-
module_info = resp.json()
453489
if "grass-module" in module_info["categories"]:
454-
# get GRASS processing type
455-
process_type = GRASS_MODULE_TYPE[process_id.split(".", 1)[0]]
456-
# check if module has input/map parameter
457-
has_input = any(
458-
param.get("name") in {"input", "map"}
459-
for param in module_info.get("parameters", [])
460-
)
461-
if not has_input or process_type not in {"raster", "vector"}:
462-
msg = f"Process execution of <{process_id}> not supported."
463-
res = jsonify(
464-
SimpleStatusCodeResponseModel(
465-
status=400,
466-
message=msg,
467-
),
468-
)
469-
return make_response(res, 400)
470-
471490
# adjust PC list
472491
pc_list = pc["list"]
473492
process = pc_list[0]
@@ -479,9 +498,7 @@ def post_process_execution(
479498
)
480499
if postbody.get("inputs").get("bounding_box"):
481500
# adjust PC if bounding box given
482-
bounding_box = (
483-
postbody.get("inputs").get("bounding_box").get("bbox")
484-
)
501+
bounding_box = postbody.get("inputs").get("bounding_box")
485502
_add_regionsetting_via_bbox_to_pc_list(pc_list, bounding_box)
486503
if process_type == "vector":
487504
_add_vclip_to_pc_list(pc_list, input_map)
@@ -491,7 +508,7 @@ def post_process_execution(
491508
_add_exporter_to_pc_list(process_type, pc_list, process, input_map)
492509
elif postbody.get("inputs").get("bounding_box"):
493510
# adjust PC if bounding box given
494-
bounding_box = postbody.get("inputs").get("bounding_box").get("bbox")
511+
bounding_box = postbody.get("inputs").get("bounding_box")
495512
# adjust PC list
496513
pc_list = pc["list"]
497514
# Currently always region set via bounding box (if given).

tests/integrationtests/test_processes_execution.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"inputs": {
2929
"url_to_geojson_point": "https://raw.githubusercontent.com/"
3030
"mmacata/pagestest/gh-pages/pointInBonn.geojson",
31-
"bounding_box": {"bbox": [6154000, 4464000, 6183000, 4490000]},
31+
"bounding_box": [6154000, 4464000, 6183000, 4490000],
3232
},
3333
"outputs": {"result": {"transmissionMode": "reference"}},
3434
"response": "document",
@@ -38,7 +38,7 @@
3838
"inputs": {
3939
"url_to_geojson_point": "https://raw.githubusercontent.com/"
4040
"mmacata/pagestest/gh-pages/pointInBonn.geojson",
41-
"bounding_box": {"bbox": [6154000, 4464000]},
41+
"bounding_box": [6154000, 4464000],
4242
},
4343
"outputs": {"result": {"transmissionMode": "reference"}},
4444
"response": "document",

tests/integrationtests/test_processes_execution_grass_module.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"output": "boundary_county_1_buf",
3535
"cats": "1",
3636
"distance": 2,
37-
"bounding_box": {"bbox": [370000, 277000, 410000, 300000]},
37+
"bounding_box": [370000, 277000, 410000, 300000],
3838
},
3939
"outputs": {"result": {"transmissionMode": "reference"}},
4040
"response": "document",
@@ -192,10 +192,9 @@ def test_post_process_execution_not_supported_module_1(self) -> None:
192192
assert isinstance(resp, Response)
193193
assert resp.status_code == 400
194194
assert hasattr(resp, "json")
195-
assert "detail" in resp.json
196-
assert resp.json["detail"] == (
197-
"Missing required input parameter <format> for process "
198-
"<g.region>."
195+
assert (
196+
resp.json["message"] == "Process execution of <g.region> not "
197+
"supported."
199198
)
200199

201200
@pytest.mark.integrationtest

0 commit comments

Comments
 (0)