From 00a08e05baa141281a21fa52127de1ca2f38539a Mon Sep 17 00:00:00 2001 From: Tom Kralidis Date: Thu, 22 Jul 2021 09:13:09 -0400 Subject: [PATCH] update OAProc content models (#735) --- .../data-publishing/ogcapi-processes.rst | 6 +- pygeoapi/api.py | 18 ++-- pygeoapi/process/hello_world.py | 90 ++++++++----------- pygeoapi/templates/processes/process.html | 28 +++--- tests/test_api.py | 83 +++++------------ 5 files changed, 84 insertions(+), 141 deletions(-) diff --git a/docs/source/data-publishing/ogcapi-processes.rst b/docs/source/data-publishing/ogcapi-processes.rst index a063d40..b777bc9 100644 --- a/docs/source/data-publishing/ogcapi-processes.rst +++ b/docs/source/data-publishing/ogcapi-processes.rst @@ -65,11 +65,11 @@ Processing examples - show all jobs for the ``hello-world`` process - http://localhost:5000/processes/hello-world/jobs - execute a job for the ``hello-world`` process - - ``curl -X POST "http://localhost:5000/processes/hello-world/jobs" -H "Content-Type: application/json" -d "{\"inputs\":[{\"id\":\"name\",\"type\":\"text/plain\",\"value\":\"hi there2\"}]}"`` + - ``curl -X POST "http://localhost:5000/processes/hello-world/execution" -H "Content-Type: application/json" -d "{\"inputs\":{\"name\": \"hi there2\"}}"`` - execute a job for the ``hello-world`` process with a raw response - - ``curl -X POST "http://localhost:5000/processes/hello-world/jobs?response=raw" -H "Content-Type: application/json" -d "{\"inputs\":[{\"id\":\"name\",\"type\":\"text/plain\",\"value\":\"hi there2\"}]}"`` + - ``curl -X POST "http://localhost:5000/processes/hello-world/execution?response=raw" -H "Content-Type: application/json" -d "{\"inputs\":{\"name\": \"hi there2\"}}"`` - execute a job for the ``hello-world`` process in asynchronous mode - - ``curl -X POST "http://localhost:5000/processes/hello-world/jobs" -H "Content-Type: application/json" -d "{\"mode\": \"async\", \"inputs\":[{\"id\":\"name\",\"type\":\"text/plain\",\"value\":\"hi there2\"}]}"`` + - ``curl -X POST "http://localhost:5000/processes/hello-world/execution" -H "Content-Type: application/json" -d "{\"mode\": \"async\", \"inputs\":{\"name\": \"hi there2\"}}"`` .. todo:: add more examples once OAProc implementation is complete diff --git a/pygeoapi/api.py b/pygeoapi/api.py index ca8c1a7..8ebcdc7 100644 --- a/pygeoapi/api.py +++ b/pygeoapi/api.py @@ -2642,15 +2642,13 @@ class API: try: data_dict = {} - for input_ in data.get('inputs', []): - id_ = input_['id'] - value = input_['value'] - if id_ not in data_dict: - data_dict[id_] = value - elif id_ in data_dict and isinstance(data_dict[id_], list): - data_dict[id_].append(value) + for key, value in data.get('inputs', {}).items(): + if key not in data_dict: + data_dict[key] = value + elif key in data_dict and isinstance(data_dict[key], list): + data_dict[key].append(value) else: - data_dict[id_] = [data_dict[id_], value] + data_dict[key] = [data_dict[key], value] except KeyError: # Return 4XX client error for missing 'id' or 'value' in an input msg = 'invalid request data' @@ -2669,6 +2667,10 @@ class API: if is_async: LOGGER.debug('Asynchronous request mode detected') + if is_async and not self.manager.is_async: + LOGGER.debug('async manager not configured/enabled') + is_async = False + try: LOGGER.debug('Executing process') mime_type, outputs, status = self.manager.execute_process( diff --git a/pygeoapi/process/hello_world.py b/pygeoapi/process/hello_world.py index 6cb409b..590cecb 100644 --- a/pygeoapi/process/hello_world.py +++ b/pygeoapi/process/hello_world.py @@ -58,61 +58,47 @@ PROCESS_METADATA = { 'href': 'https://example.org/process', 'hreflang': 'en-US' }], - 'inputs': [{ - 'id': 'name', - 'title': 'Name', - 'abstract': 'The name of the person or entity that you wish to be' - 'echoed back as an output', - 'input': { - 'literalDataDomain': { - 'dataType': 'string', - 'valueDefinition': { - 'anyValue': True - } - } + 'inputs': { + 'name': { + 'title': 'Name', + 'description': 'The name of the person or entity that you wish to' + 'be echoed back as an output', + 'schema': { + 'type': 'string' + }, + 'minOccurs': 1, + 'maxOccurs': 1, + 'metadata': None, # TODO how to use? + 'keywords': ['full name', 'personal'] }, - 'minOccurs': 1, - 'maxOccurs': 1, - 'metadata': None, # TODO how to use? - 'keywords': ['full name', 'personal'] - }, { - 'id': 'message', - 'title': 'Message', - 'abstract': 'An optional message to echo as well', - 'input': { - 'literalDataDomain': { - 'dataType': 'string', - 'valueDefinition': { - 'anyValue': True - } - } - }, - 'minOccurs': 0, - 'maxOccurs': 1, - 'metadata': None, - 'keywords': ['message'] - }], - 'outputs': [{ - 'id': 'echo', - 'title': 'Hello, world', - 'description': 'A "hello world" echo with the name and (optional)' - 'message submitted for processing', - 'output': { - 'formats': [{ - 'mimeType': 'application/json' - }] + 'message': { + 'title': 'Message', + 'description': 'An optional message to echo as well', + 'schema': { + 'type': 'string' + }, + 'minOccurs': 0, + 'maxOccurs': 1, + 'metadata': None, + 'keywords': ['message'] } - }], + }, + 'outputs': { + 'echo': { + 'title': 'Hello, world', + 'description': 'A "hello world" echo with the name and (optional)' + ' message submitted for processing', + 'schema': { + 'type': 'object', + 'contentMediaType': 'application/json' + } + } + }, 'example': { - 'inputs': [{ - 'id': 'name', - 'value': 'World', - 'type': 'text/plain' - }, { - 'id': 'message', - 'value': 'An optional message.', - 'type': 'text/plain' - }] + 'inputs': { + 'name': 'World', + 'messsage': 'An optional message.', + } } } diff --git a/pygeoapi/templates/processes/process.html b/pygeoapi/templates/processes/process.html index f8f30a9..2dc87a8 100644 --- a/pygeoapi/templates/processes/process.html +++ b/pygeoapi/templates/processes/process.html @@ -27,27 +27,19 @@ - {% for input_ in data['inputs'] %} + {% for key, value in data['inputs'].items() %} - {{ input_.id }} + {{ key }} - {{ input_.title|striptags|truncate }} + {{ value.title|striptags|truncate }} - - {% with literalDataDomain = input_.input.literalDataDomain %} - {% if literalDataDomain %} - {{ literalDataDomain.dataType }} - {% else %} - {% for format in input_.input.formats %} - {{ format.mimeType }}{% if not loop.last %},{% endif %} - {% endfor %} - {% endif %} - {% endwith %} + + {{ value.schema.type }} - {{ input_.abstract }} + {{ value.description }} {% endfor %} @@ -65,12 +57,12 @@ - {% for output_ in data['outputs'] %} + {% for key, value in data['outputs'].items() %} - {{ output_['id'] }} - {{ output_['title'] }} + {{ key }} + {{ value.title }} - {{ output_['description'] | striptags | truncate }} + {{ value.description | striptags | truncate }} {% endfor %} diff --git a/tests/test_api.py b/tests/test_api.py index 9c6ba12..cb51774 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1047,50 +1047,33 @@ def test_describe_processes(config, api_): def test_execute_process(config, api_): req_body = { - 'inputs': [{ - 'id': 'name', - 'value': 'Test' - }] + 'inputs': { + 'name': 'Test' + } } req_body_2 = { - 'inputs': [{ - 'id': 'name', - 'value': 'Tést' - }] + 'inputs': { + 'name': 'Tést' + } } req_body_3 = { - 'inputs': [{ - 'id': 'name', - 'value': 'Tést' - }, { - 'id': 'message', - 'value': 'This is a test.' - }] + 'inputs': { + 'name': 'Tést', + 'message': 'This is a test.' + } } req_body_4 = { - 'inputs': [{ - 'id': 'foo', - 'value': 'Tést' - }] + 'inputs': { + 'foo': 'Tést' + } } req_body_5 = { - 'inputs': [] + 'inputs': {} } req_body_6 = { - 'inputs': [{ - 'id': 'name', - 'value': None - }] - } - req_body_7 = { - 'inputs': [{ - 'id': 'name' - }] - } - req_body_8 = { - 'inputs': [{ - 'value': 'Test' - }] + 'inputs': { + 'name': None + } } cleanup_jobs = set() @@ -1180,24 +1163,6 @@ def test_execute_process(config, api_): cleanup_jobs.add(tuple(['hello-world', rsp_headers['Location'].split('/')[-1]])) - req = mock_request(data=req_body_7) - rsp_headers, code, response = api_.execute_process(req, 'hello-world') - - data = json.loads(response) - assert code == 400 - assert 'Location' not in rsp_headers - assert data['code'] == 'InvalidParameterValue' - assert data['description'] == 'invalid request data' - - req = mock_request(data=req_body_8) - rsp_headers, code, response = api_.execute_process(req, 'hello-world') - - data = json.loads(response) - assert code == 400 - assert 'Location' not in rsp_headers - assert data['code'] == 'InvalidParameterValue' - assert data['description'] == 'invalid request data' - req = mock_request(data=req_body) rsp_headers, code, response = api_.execute_process(req, 'goodbye-world') @@ -1241,18 +1206,16 @@ def test_delete_process_job(api_): assert code == 404 req_body_sync = { - 'inputs': [{ - 'id': 'name', - 'value': 'Sync Test Deletion' - }] + 'inputs': { + 'name': 'Sync Test Deletion' + } } req_body_async = { 'mode': 'async', - 'inputs': [{ - 'id': 'name', - 'value': 'Async Test Deletion' - }] + 'inputs': { + 'name': 'Async Test Deletion' + } } req = mock_request(data=req_body_sync)