Skip to content

Commit c1008a0

Browse files
authored
Merge pull request #30 from actinia-org/enhance-pc-transform
Input check enhancements and add minOccurs to required parameters
2 parents 4903260 + 9d9c373 commit c1008a0

File tree

5 files changed

+68
-18
lines changed

5 files changed

+68
-18
lines changed

src/actinia_ogc_api_processes_plugin/core/process_description.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def update_resp(resp_json: dict) -> dict:
6464
project_input = {
6565
"description": "Name of GRASS GIS project to use",
6666
"name": "project",
67-
"optional": False,
67+
"optional": True,
6868
"schema": {"type": "string"},
6969
"default": ACTINIA.default_project,
7070
}
@@ -84,6 +84,10 @@ def update_resp(resp_json: dict) -> dict:
8484
continue
8585
# keep parameter fields but remove the redundant 'name' field
8686
value = {k: v for k, v in p.items() if k != "name"}
87+
# transform 'optional' to 'minOccurs
88+
optional = value.pop("optional", None)
89+
if optional is False:
90+
value["minOccurs"] = 1
8791
# subtype not allowed in validator, removing it
8892
if "schema" in value:
8993
value["schema"].pop("subtype", None)

src/actinia_ogc_api_processes_plugin/core/process_execution.py

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,21 @@ def _invalid_inputs(module_info: dict, inputs: dict):
296296
return invalid, msg
297297

298298

299+
def _missing_inputs(module_info: dict, inputs: dict):
300+
"""Check if required inputs are missing."""
301+
params = module_info.get("parameters", [])
302+
params += module_info.get("returns", [])
303+
required_params = {p.get("name") for p in params if not p.get("optional")}
304+
return [param for param in required_params if param not in inputs]
305+
306+
307+
def is_valid_postbody(postbody: dict) -> bool:
308+
"""Check if postbody is valid."""
309+
allowed_keys = {"inputs", "outputs", "response", "subscriber"}
310+
wrong_key = any(key not in allowed_keys for key, value in postbody.items())
311+
return not wrong_key
312+
313+
299314
def _validate_string_input(
300315
invalid: list,
301316
key: str,
@@ -359,9 +374,20 @@ def _check_input_by_reference(postbody: dict) -> dict:
359374

360375
def post_process_execution(
361376
process_id: str | None = None,
362-
postbody: str | None = None,
377+
postbody: dict | None = None,
363378
):
364379
"""Start job for given process_id."""
380+
if not is_valid_postbody(postbody):
381+
res = jsonify(
382+
{
383+
"type": "InvalidRequestBody",
384+
"title": "Invalid request body",
385+
"status": 400,
386+
"detail": "Request body contains invalid keys.",
387+
},
388+
)
389+
return make_response(res, 400)
390+
365391
# Authentication for actinia
366392
auth = request.authorization
367393
kwargs = dict()
@@ -383,10 +409,32 @@ def post_process_execution(
383409
invalid_inp_str = ", ".join(str(x) for x in invalid_inputs)
384410
msg = f"Invalid input <{invalid_inp_str}> for process <{process_id}>."
385411
res = jsonify(
386-
SimpleStatusCodeResponseModel(
387-
status=400,
388-
message=(msg + " " + detail_msg),
389-
),
412+
{
413+
"type": "InvalidInput",
414+
"title": "Invalid input",
415+
"status": 400,
416+
"detail": (msg + " " + detail_msg),
417+
},
418+
)
419+
return make_response(res, 400)
420+
421+
missing_inputs = _missing_inputs(
422+
resp.json(),
423+
postbody.get("inputs", {}),
424+
)
425+
if missing_inputs:
426+
missing_inputs_str = ", ".join(str(x) for x in missing_inputs)
427+
msg = (
428+
f"Missing required input parameter <{missing_inputs_str}> for "
429+
f"process <{process_id}>."
430+
)
431+
res = jsonify(
432+
{
433+
"type": "InvalidInput",
434+
"title": "Missing required input",
435+
"status": 400,
436+
"detail": msg,
437+
},
390438
)
391439
return make_response(res, 400)
392440

tests/integrationtests/test_processes_execution.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,9 @@ def test_post_process_execution_with_bbox_error(self) -> None:
144144
assert isinstance(resp, Response)
145145
assert resp.status_code == 400
146146
assert hasattr(resp, "json")
147-
assert "message" in resp.json
148-
assert (
149-
"'bbox' should be a list of 4 or 6 numbers."
150-
in resp.json["message"]
151-
)
147+
assert "type" in resp.json
148+
assert "detail" in resp.json
149+
assert "bounding_box" in resp.json["detail"]
152150

153151
@pytest.mark.integrationtest
154152
def test_post_process_execution_invalid_process_id(self) -> None:

tests/integrationtests/test_processes_execution_grass_module.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def test_post_process_execution_vbuffer_array_error(self) -> None:
158158
assert resp.status_code == 400
159159
assert (
160160
"Input parameter 'type' should be type array"
161-
in resp.json["message"]
161+
in resp.json["detail"]
162162
)
163163

164164
@pytest.mark.integrationtest
@@ -192,10 +192,10 @@ 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 "message" in resp.json
196-
assert (
197-
resp.json["message"] == "Process execution of <g.region> not "
198-
"supported."
195+
assert "detail" in resp.json
196+
assert resp.json["detail"] == (
197+
"Missing required input parameter <format> for process "
198+
"<g.region>."
199199
)
200200

201201
@pytest.mark.integrationtest

tests/integrationtests/test_processes_execution_input_by_reference.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,5 +141,5 @@ def test_post_process_execution_input_by_ref_error(self) -> None:
141141
assert isinstance(resp, Response)
142142
assert resp.status_code == 400
143143
assert hasattr(resp, "json")
144-
assert "message" in resp.json
145-
assert "'.geojson' which is not supported." in resp.json["message"]
144+
assert "detail" in resp.json
145+
assert "'.geojson' which is not supported." in resp.json["detail"]

0 commit comments

Comments
 (0)