From 046ae04c097ab926c78709355d617a553471ba90 Mon Sep 17 00:00:00 2001 From: Nikki <17799906+nikki-t@users.noreply.github.com> Date: Tue, 2 Jul 2024 09:40:30 -0400 Subject: [PATCH 01/60] Unit test to deploy, execute, and undeploy CWL DAG --- unity-test/conftest.py | 1 + .../execution_requests/cwl_dag_request.json | 13 +++++ .../process_descriptions/cwl_dag_process.json | 49 ++++++++++++++++ unity-test/test_main.py | 58 +++++++++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 unity-test/test_data/execution_requests/cwl_dag_request.json create mode 100644 unity-test/test_data/process_descriptions/cwl_dag_process.json diff --git a/unity-test/conftest.py b/unity-test/conftest.py index 65e8776..87d69d5 100644 --- a/unity-test/conftest.py +++ b/unity-test/conftest.py @@ -62,6 +62,7 @@ def fake_filesystem(fs_session, test_directory): # pylint:disable=invalid-name os.path.join(settings.DAG_CATALOG_DIRECTORY, "cwltool_help_dag.py"), contents="test" ) fs_session.create_file(os.path.join(settings.DAG_CATALOG_DIRECTORY, "EchoProcess.py"), contents="test") + fs_session.create_file(os.path.join(settings.DAG_CATALOG_DIRECTORY, "cwl_dag.py"), contents="test") fs_session.create_dir(settings.DEPLOYED_DAGS_DIRECTORY) yield fs_session diff --git a/unity-test/test_data/execution_requests/cwl_dag_request.json b/unity-test/test_data/execution_requests/cwl_dag_request.json new file mode 100644 index 0000000..07ab590 --- /dev/null +++ b/unity-test/test_data/execution_requests/cwl_dag_request.json @@ -0,0 +1,13 @@ +{ + "inputs": { + "complexObjectInput": { + "value": { + "message": "Hello Unity" + } + } + }, + "outputs": { + "stringOutput": {} + } + } + \ No newline at end of file diff --git a/unity-test/test_data/process_descriptions/cwl_dag_process.json b/unity-test/test_data/process_descriptions/cwl_dag_process.json new file mode 100644 index 0000000..f30b844 --- /dev/null +++ b/unity-test/test_data/process_descriptions/cwl_dag_process.json @@ -0,0 +1,49 @@ +{ + "description": "This process accepts string input and echoes the input as a string output.", + "id": "cwl_dag", + "inputs": [ + { + "complexObjectInput": { + "description": "Excepts a string under message property.", + "schema": { + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "title": "Input object" + } + } + ], + "jobControlOptions": [ + "async-execute", + "sync-execute" + ], + "links": [ + { + "href": "https://processing.example.org/oapi-p/processes/CWGDAGProcess/execution", + "rel": "http://www.opengis.net/def/rel/ogc/1.0/execute", + "title": "Execute endpoint" + } + ], + "outputs": [ + { + "stringOutput": { + "schema": { + "enum": [ + "Output string value" + ], + "type": "string" + } + } + } + ], + "title": "CWL DAG Process", + "version": "1.0.0" + } + \ No newline at end of file diff --git a/unity-test/test_main.py b/unity-test/test_main.py index 4c5c8d7..6ee6c1d 100644 --- a/unity-test/test_main.py +++ b/unity-test/test_main.py @@ -129,3 +129,61 @@ def test_get_results(client, execute_process): assert response.status_code == status.HTTP_200_OK data = response.json() assert Results.model_validate(data) + + +@pytest.mark.dependency() +def test_post_deploy_process_cwl_dag(test_directory, client): + data_filename = os.path.join(test_directory, "test_data/process_descriptions/cwl_dag_process.json") + f = open(data_filename) + process_json = json.load(f) + process = Process.model_validate(process_json) + response = client.post("/processes", json=process.model_dump()) + assert response.status_code == status.HTTP_200_OK + data = response.json() + process = Process.model_validate(data) + assert process.id == "cwl_dag" + assert process.title == "CWL DAG Process" + + +@pytest.mark.dependency(depends=["test_post_deploy_process_cwl_dag"]) +def test_post_execute_process_cwl_dag(test_directory, client): + data_filename = os.path.join(test_directory, "test_data/execution_requests/cwl_dag_request.json") + f = open(data_filename) + execute_json = json.load(f) + execute = Execute.model_validate(execute_json) + response = client.post("/processes/cwl_dag/execution", json=jsonable_encoder(execute)) + assert response.status_code == status.HTTP_200_OK + data = response.json() + assert StatusInfo.model_validate(data) + assert data["processID"] == "cwl_dag" + assert data["type"] == "process" + + +@pytest.mark.dependency(depends=["test_post_execute_process_cwl_dag"]) +def test_get_status_cwl_dag(test_directory, client): + data_filename = os.path.join(test_directory, "test_data/execution_requests/cwl_dag_request.json") + f = open(data_filename) + execute_json = json.load(f) + execute = Execute.model_validate(execute_json) + execute_response = client.post("/processes/cwl_dag/execution", json=jsonable_encoder(execute)) + data = execute_response.json() + status_info = StatusInfo.model_validate(data) + job_id = status_info.jobID + + code = status.HTTP_404_NOT_FOUND + while code != status.HTTP_200_OK: + status_response = client.get(f"/jobs/{job_id}") + code = status_response.status_code + + assert status_response.status_code == status.HTTP_200_OK + data = status_response.json() + status_info = StatusInfo.model_validate(data) + assert status_info.jobID == job_id + + +@pytest.mark.dependency(depends=["test_get_status_cwl_dag"]) +def test_delete_undeploy_cwl_dag(client): + response = client.delete("/processes/cwl_dag") + assert response.status_code == status.HTTP_409_CONFLICT + response = client.delete("/processes/cwl_dag", params={"force": True}) + assert response.status_code == status.HTTP_204_NO_CONTENT From 8afdb590dd742ae13adbd59d4650a80c8e2d22f6 Mon Sep 17 00:00:00 2001 From: Nikki <17799906+nikki-t@users.noreply.github.com> Date: Tue, 2 Jul 2024 10:24:09 -0400 Subject: [PATCH 02/60] Fix new line at end of file --- .../execution_requests/cwl_dag_request.json | 17 ++-- .../process_descriptions/cwl_dag_process.json | 87 +++++++++---------- 2 files changed, 51 insertions(+), 53 deletions(-) diff --git a/unity-test/test_data/execution_requests/cwl_dag_request.json b/unity-test/test_data/execution_requests/cwl_dag_request.json index 07ab590..42db1ba 100644 --- a/unity-test/test_data/execution_requests/cwl_dag_request.json +++ b/unity-test/test_data/execution_requests/cwl_dag_request.json @@ -1,13 +1,12 @@ { - "inputs": { - "complexObjectInput": { - "value": { - "message": "Hello Unity" - } + "inputs": { + "complexObjectInput": { + "value": { + "message": "Hello Unity" } - }, - "outputs": { - "stringOutput": {} } + }, + "outputs": { + "stringOutput": {} } - \ No newline at end of file +} diff --git a/unity-test/test_data/process_descriptions/cwl_dag_process.json b/unity-test/test_data/process_descriptions/cwl_dag_process.json index f30b844..a727cb2 100644 --- a/unity-test/test_data/process_descriptions/cwl_dag_process.json +++ b/unity-test/test_data/process_descriptions/cwl_dag_process.json @@ -1,49 +1,48 @@ { - "description": "This process accepts string input and echoes the input as a string output.", - "id": "cwl_dag", - "inputs": [ - { - "complexObjectInput": { - "description": "Excepts a string under message property.", - "schema": { - "properties": { - "message": { - "type": "string" - } - }, - "required": [ - "message" - ], - "type": "object" + "description": "This process accepts string input and echoes the input as a string output.", + "id": "cwl_dag", + "inputs": [ + { + "complexObjectInput": { + "description": "Excepts a string under message property.", + "schema": { + "properties": { + "message": { + "type": "string" + } }, - "title": "Input object" - } - } - ], - "jobControlOptions": [ - "async-execute", - "sync-execute" - ], - "links": [ - { - "href": "https://processing.example.org/oapi-p/processes/CWGDAGProcess/execution", - "rel": "http://www.opengis.net/def/rel/ogc/1.0/execute", - "title": "Execute endpoint" + "required": [ + "message" + ], + "type": "object" + }, + "title": "Input object" } - ], - "outputs": [ - { - "stringOutput": { - "schema": { - "enum": [ - "Output string value" - ], - "type": "string" - } + } + ], + "jobControlOptions": [ + "async-execute", + "sync-execute" + ], + "links": [ + { + "href": "https://processing.example.org/oapi-p/processes/CWGDAGProcess/execution", + "rel": "http://www.opengis.net/def/rel/ogc/1.0/execute", + "title": "Execute endpoint" + } + ], + "outputs": [ + { + "stringOutput": { + "schema": { + "enum": [ + "Output string value" + ], + "type": "string" } } - ], - "title": "CWL DAG Process", - "version": "1.0.0" - } - \ No newline at end of file + } + ], + "title": "CWL DAG Process", + "version": "1.0.0" +} From 5dc5589424ebe473038d3a1f042ddad7d8f461b0 Mon Sep 17 00:00:00 2001 From: Nikki <17799906+nikki-t@users.noreply.github.com> Date: Fri, 5 Jul 2024 08:04:42 -0400 Subject: [PATCH 03/60] Parametrize unit test functions to run on multiple CWL/DAGs --- unity-test/conftest.py | 23 +- .../execution_requests/EchoProcess.json | 121 ++++++ .../{cwl_dag_request.json => cwl_dag.json} | 0 ...ol_help_dag.json => cwltool_help_dag.json} | 0 .../{cwl_dag_process.json => cwl_dag.json} | 0 .../cwltool_help_dag.json | 360 ++++++++++++++++++ unity-test/test_main.py | 72 ++-- 7 files changed, 535 insertions(+), 41 deletions(-) create mode 100644 unity-test/test_data/execution_requests/EchoProcess.json rename unity-test/test_data/execution_requests/{cwl_dag_request.json => cwl_dag.json} (100%) rename unity-test/test_data/execution_requests/{execute_cwltool_help_dag.json => cwltool_help_dag.json} (100%) rename unity-test/test_data/process_descriptions/{cwl_dag_process.json => cwl_dag.json} (100%) create mode 100644 unity-test/test_data/process_descriptions/cwltool_help_dag.json diff --git a/unity-test/conftest.py b/unity-test/conftest.py index 87d69d5..b983019 100644 --- a/unity-test/conftest.py +++ b/unity-test/conftest.py @@ -1,5 +1,7 @@ +import glob import json import os +import pathlib import re import fakeredis @@ -27,6 +29,11 @@ Base.metadata.create_all(bind=engine) +TEST_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_data") +PROCESS_FILES = glob.glob(f"{TEST_DIR}/process_descriptions/*.json") +EXECUTION_FILES = glob.glob(f"{TEST_DIR}/execution_requests/*.json") + + def override_get_db(): try: db = TestingSessionLocal() @@ -422,17 +429,16 @@ def mock_patch_existing_running_dag_dagrun_task(requests_mock): ) -@pytest.fixture(scope="function") -def deploy_process(test_directory, client): - data_filename = os.path.join(test_directory, "..", "process_descriptions", "cwltool_help_dag.json") - f = open(data_filename) +@pytest.fixture(scope="function", params=PROCESS_FILES) +def deploy_process(test_directory, client, request): + f = open(request.param) process_json = json.load(f) process = Process.model_validate(process_json) response = client.post("/processes", json=process.model_dump()) assert response.status_code == status.HTTP_200_OK data = response.json() process = Process.model_validate(data) - assert process.id == "cwltool_help_dag" + assert process.id == pathlib.Path(request.param).stem yield process @@ -440,10 +446,9 @@ def deploy_process(test_directory, client): assert response.status_code == status.HTTP_204_NO_CONTENT -@pytest.fixture(scope="function") -def execute_process(test_directory, mock_post_existing_dag_new_dagrun, client, deploy_process): - data_filename = os.path.join(test_directory, "test_data/execution_requests/execute_cwltool_help_dag.json") - f = open(data_filename) +@pytest.fixture(scope="function", params=EXECUTION_FILES) +def execute_process(test_directory, mock_post_existing_dag_new_dagrun, client, deploy_process, request): + f = open(request.param) execute_json = json.load(f) execute = Execute.model_validate(execute_json) response = client.post(f"/processes/{deploy_process.id}/execution", json=jsonable_encoder(execute)) diff --git a/unity-test/test_data/execution_requests/EchoProcess.json b/unity-test/test_data/execution_requests/EchoProcess.json new file mode 100644 index 0000000..65ce3dd --- /dev/null +++ b/unity-test/test_data/execution_requests/EchoProcess.json @@ -0,0 +1,121 @@ +{ + "inputs": { + "arrayInput": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "boundingBoxInput": { + "bbox": [ + 51.9, + 7, + 52, + 7.1 + ], + "crs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + }, + "complexObjectInput": { + "value": { + "property1": "value1", + "property2": "value2", + "property5": true + } + }, + "dateInput": "2021-03-06T07:21:00", + "doubleInput": 3.14159, + "featureCollectionInput": { + "mediaType": "application/gml+xml; version=3.2", + "value": "..." + }, + "geometryInput": [ + { + "mediaType": "application/gml+xml; version=3.2", + "value": "-77.024519 38.810529 -77.024635 38.810973 -77.024704 38.810962 -77.024776 38.811239 -77.024957 38.81121 -77.024905 38.811012 -77.024905 38.811012 -77.024865 38.810857 -77.025024 38.810832 -77.025071 38.811012 -77.025203 38.810992 -77.02506 38.810444 -77.024519 38.810529" + }, + { + "value": { + "coordinates": [ + [ + [ + -176.5814819, + -44.10896301 + ], + [ + -176.5818024, + -44.10964584 + ], + [ + -176.5844116, + -44.11236572 + ], + [ + -176.5935974, + -44.11021805 + ], + [ + -176.5973511, + -44.10743332 + ], + [ + -176.5950928, + -44.10562134 + ], + [ + -176.5858459, + -44.1043396 + ], + [ + -176.5811157, + -44.10667801 + ], + [ + -176.5814819, + -44.10896301 + ] + ] + ], + "type": "Polygon" + } + } + ], + "imagesInput": [ + { + "href": "https://www.someserver.com/ogcapi/Daraa/collections/Daraa_DTED/styles/Topographic/coverage?...", + "type": "image/tiff; application=geotiff" + }, + { + "encoding": "base64", + "mediaType": "image/jp2", + "value": "VBORw0KGgoAAAANSUhEUgAABvwAAAa4CAYAAABMB35kAAABhGlDQ1BJQ0MgcHJvZmlsZQAA\nKJF9kT1Iw0AcxV9TpSL1A+xQxCFDdbIgKuKoVShChVArtOpgcumH0KQhSXFxFFwLDn4sVh1c\nnHV1cBUEwQ8QNzcnRRcp8X9JoUWMB8f9eHfvcfcOEOplplkdY4Cm22Y6mRCzuRUx9IogouhH\n ... \nj3Z5mX7/PCPVRJV92rpHK24xcJrzk20+tkeYlCPqcZNO3Lpni1OJWatPCcmgGDEqx7Om6lfa\nppM4k4BTe9+bsn3L9/9/yWhA0PwQGW8ipCZsnZt9lsdrYEM8z/M8z/M8z/M8z/M8z/MzLWY1\nAAAACUlEQVQ871H6P6JI+TxS5Wn2AAAAAElFTkSuQmCC" + } + ], + "measureInput": { + "value": { + "measurement": 10.3, + "reference": "https://ucum.org/ucum-essence.xml", + "uom": "m" + } + }, + "stringInput": "Value2" + }, + "outputs": { + "arrayOutput": {}, + "boundingBoxOutput": {}, + "complexObjectOutput": {}, + "dateOutput": {}, + "doubleOutput": {}, + "featureCollectionOutput": {}, + "geometryOutput": {}, + "imageOutput": { + "format": { + "mediaType": "image/tiff; application=geotiff" + } + }, + "measureOutput": {}, + "stringOutput": {} + } + } + \ No newline at end of file diff --git a/unity-test/test_data/execution_requests/cwl_dag_request.json b/unity-test/test_data/execution_requests/cwl_dag.json similarity index 100% rename from unity-test/test_data/execution_requests/cwl_dag_request.json rename to unity-test/test_data/execution_requests/cwl_dag.json diff --git a/unity-test/test_data/execution_requests/execute_cwltool_help_dag.json b/unity-test/test_data/execution_requests/cwltool_help_dag.json similarity index 100% rename from unity-test/test_data/execution_requests/execute_cwltool_help_dag.json rename to unity-test/test_data/execution_requests/cwltool_help_dag.json diff --git a/unity-test/test_data/process_descriptions/cwl_dag_process.json b/unity-test/test_data/process_descriptions/cwl_dag.json similarity index 100% rename from unity-test/test_data/process_descriptions/cwl_dag_process.json rename to unity-test/test_data/process_descriptions/cwl_dag.json diff --git a/unity-test/test_data/process_descriptions/cwltool_help_dag.json b/unity-test/test_data/process_descriptions/cwltool_help_dag.json new file mode 100644 index 0000000..b316a98 --- /dev/null +++ b/unity-test/test_data/process_descriptions/cwltool_help_dag.json @@ -0,0 +1,360 @@ +{ + "description": "This process accepts and number of input and simple echoes each input as an output.", + "id": "cwltool_help_dag", + "inputs": [ + { + "arrayInput": { + "description": "This is an example of a single process input that is an array of values. In this case, the input array would be interpreted as a single value and not as individual inputs.", + "schema": { + "items": { + "type": "integer" + }, + "maxItems": 10, + "minItems": 2, + "type": "array" + }, + "title": "Array Input Example" + }, + "boundingBoxInput": { + "description": "This is an example of a BBOX literal input.", + "schema": { + "allOf": [ + { + "format": "ogc-bbox" + }, + { + "$ref": "../../openapi/schemas/bbox.yaml" + } + ] + }, + "title": "Bounding Box Input Example" + }, + "complexObjectInput": { + "description": "This is an example of a complex object input.", + "schema": { + "properties": { + "property1": { + "type": "string" + }, + "property2": { + "format": "uri", + "type": "string" + }, + "property3": { + "type": "number" + }, + "property4": { + "format": "date-time", + "type": "string" + }, + "property5": { + "type": "boolean" + } + }, + "required": [ + "property1", + "property5" + ], + "type": "object" + }, + "title": "Complex Object Input Example" + }, + "dateInput": { + "description": "This is an example of a DATE literal input.", + "schema": { + "format": "date-time", + "type": "string" + }, + "title": "Date Literal Input Example" + }, + "doubleInput": { + "description": "This is an example of a DOUBLE literal input that is bounded between a value greater than 0 and 10. The default value is 5.", + "schema": { + "default": 5, + "exclusiveMinimum": true, + "format": "double", + "maximum": 10, + "minimum": 0, + "type": "number" + }, + "title": "Bounded Double Literal Input Example" + }, + "featureCollectionInput": { + "description": "This is an example of an input that is a feature collection that can be encoded in one of three ways: as a GeoJSON feature collection, as a GML feature collection retrieved from a WFS or as a KML document.", + "schema": { + "oneOf": [ + { + "contentMediaType": "application/gml+xml; version=3.2", + "type": "string" + }, + { + "contentMediaType": "application/vnd.google-earth.kml+xml", + "contentSchema": "https://schemas.opengis.net/kml/2.3/ogckml23.xsd", + "type": "string" + }, + { + "allOf": [ + { + "format": "geojson-feature-collection" + }, + { + "$ref": "https://geojson.org/schema/FeatureCollection.json" + } + ] + } + ] + }, + "title": "Feature Collection Input Example." + }, + "geometryInput": { + "description": "This is an example of a geometry input. In this case the geometry can be expressed as a GML of GeoJSON geometry.", + "maxOccurs": 5, + "minOccurs": 2, + "schema": { + "oneOf": [ + { + "contentMediaType": "application/gml+xml; version=3.2", + "contentSchema": "http://schemas.opengis.net/gml/3.2.1/geometryBasic2d.xsd", + "type": "string" + }, + { + "format": "geojson-geometry" + } + ] + }, + "title": "Geometry input" + }, + "imagesInput": { + "description": "This is an example of an image input. In this case, the input is an array of up to 150 images that might, for example, be a set of tiles. The oneOf[] conditional is used to indicate the acceptable image content types; GeoTIFF and JPEG 2000 in this case. Each input image in the input array can be included inline in the execute request as a base64-encoded string or referenced using the link.yaml schema. The use of a base64-encoded string is implied by the specification and does not need to be specified in the definition of the input.", + "maxOccurs": 150, + "minOccurs": 1, + "schema": { + "oneOf": [ + { + "contentEncoding": "binary", + "contentMediaType": "image/tiff; application=geotiff", + "type": "string" + }, + { + "contentEncoding": "binary", + "contentMediaType": "image/jp2", + "type": "string" + } + ] + }, + "title": "Inline Images Value Input" + }, + "measureInput": { + "description": "This is an example of a NUMERIC literal with an associated unit of measure.", + "schema": { + "properties": { + "measurement": { + "type": "number" + }, + "reference": { + "format": "uri", + "type": "string" + }, + "uom": { + "type": "string" + } + }, + "required": [ + "measurement", + "uom" + ], + "type": "object" + }, + "title": "Numerical Value with UOM Example" + }, + "stringInput": { + "description": "This is an example of a STRING literal input.", + "schema": { + "enum": [ + "Value1", + "Value2", + "Value3" + ], + "type": "string" + }, + "title": "String Literal Input Example" + } + } + ], + "jobControlOptions": [ + "async-execute", + "sync-execute" + ], + "links": [ + { + "href": "https://processing.example.org/oapi-p/processes/EchoProcess/execution", + "rel": "http://www.opengis.net/def/rel/ogc/1.0/execute", + "title": "Execute endpoint" + } + ], + "outputs": [ + { + "arrayOutput": { + "schema": { + "items": { + "type": "integer" + }, + "maxItems": 10, + "minItems": 2, + "type": "array" + } + }, + "boundingBoxOutput": { + "schema": { + "allOf": [ + { + "format": "ogc-bbox" + }, + { + "$ref": "../../openapi/schemas/bbox.yaml" + } + ] + } + }, + "complexObjectOutput": { + "schema": { + "properties": { + "property1": { + "type": "string" + }, + "property2": { + "format": "uri", + "type": "string" + }, + "property3": { + "type": "number" + }, + "property4": { + "format": "date-time", + "type": "string" + }, + "property5": { + "type": "boolean" + } + }, + "required": [ + "property1", + "property5" + ], + "type": "object" + } + }, + "dateOutput": { + "schema": { + "format": "date-time", + "type": "string" + } + }, + "doubleOutput": { + "schema": { + "default": 5, + "exclusiveMinimum": true, + "format": "double", + "maximum": 10, + "minimum": 0, + "type": "number" + } + }, + "featureCollectionOutput": { + "schema": { + "oneOf": [ + { + "contentMediaType": "application/gml+xml; version=3.2", + "type": "string" + }, + { + "contentMediaType": "application/vnd.google-earth.kml+xml", + "contentSchema": "https://schemas.opengis.net/kml/2.3/ogckml23.xsd", + "type": "string" + }, + { + "allOf": [ + { + "format": "geojson-feature-collection" + }, + { + "$ref": "https://geojson.org/schema/FeatureCollection.json" + } + ] + } + ] + } + }, + "geometryOutput": { + "schema": { + "oneOf": [ + { + "contentMediaType": "application/gml+xml", + "contentSchema": "http://schemas.opengis.net/gml/3.2.1/geometryBasic2d.xsd", + "type": "string" + }, + { + "allOf": [ + { + "format": "geojson-geometry" + }, + { + "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/geometryGeoJSON.yaml" + } + ] + } + ] + } + }, + "imagesOutput": { + "schema": { + "oneOf": [ + { + "contentEncoding": "binary", + "contentMediaType": "image/tiff; application=geotiff", + "type": "string" + }, + { + "contentEncoding": "binary", + "contentMediaType": "image/jp2", + "type": "string" + } + ] + } + }, + "measureOutput": { + "schema": { + "properties": { + "measurement": { + "type": "number" + }, + "reference": { + "format": "uri", + "type": "string" + }, + "uom": { + "type": "string" + } + }, + "required": [ + "measurement", + "uom" + ], + "type": "object" + } + }, + "stringOutput": { + "schema": { + "enum": [ + "Value1", + "Value2", + "Value3" + ], + "type": "string" + } + } + } + ], + "title": "Echo Process", + "version": "1.0.0" +} diff --git a/unity-test/test_main.py b/unity-test/test_main.py index 6ee6c1d..101a7ed 100644 --- a/unity-test/test_main.py +++ b/unity-test/test_main.py @@ -1,5 +1,7 @@ +import glob import json import os +import pathlib import pytest from fastapi import status @@ -18,6 +20,10 @@ ) from app.schemas.unity_sps import HealthCheck +TEST_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_data") +PROCESS_FILES = glob.glob(f"{TEST_DIR}/process_descriptions/*.json") +EXECUTION_FILES = glob.glob(f"{TEST_DIR}/execution_requests/*.json") + def test_get_landing_page(client): response = client.get("/") @@ -46,30 +52,31 @@ def test_get_conformance_declaration(client): ] -@pytest.mark.dependency() -def test_post_deploy_process(test_directory, client): - data_filename = os.path.join(test_directory, "test_data/process_descriptions/EchoProcess.json") - f = open(data_filename) +@pytest.mark.parametrize("process_filename", PROCESS_FILES) +@pytest.mark.dependency(name="test_post_deploy_process") +def test_post_deploy_process(client, process_filename): + f = open(process_filename) process_json = json.load(f) process = Process.model_validate(process_json) response = client.post("/processes", json=process.model_dump()) assert response.status_code == status.HTTP_200_OK data = response.json() process = Process.model_validate(data) - assert process.id == "EchoProcess" + assert process.id == pathlib.Path(process_filename).stem +@pytest.mark.parametrize("process_filename", PROCESS_FILES) @pytest.mark.dependency(depends=["test_post_deploy_process"]) -def test_delete_undeploy_process(client): - response = client.delete("/processes/EchoProcess") +def test_delete_undeploy_process(client, process_filename): + process_id = pathlib.Path(process_filename).stem + response = client.delete(f"/processes/{process_id}") assert response.status_code == status.HTTP_409_CONFLICT - response = client.delete("/processes/EchoProcess", params={"force": True}) + response = client.delete(f"/processes/{process_id}", params={"force": True}) assert response.status_code == status.HTTP_204_NO_CONTENT def test_post_execute_process(test_directory, client, deploy_process): - data_filename = os.path.join(test_directory, "test_data/execution_requests/execute_cwltool_help_dag.json") - f = open(data_filename) + f = open(os.path.join(test_directory, f"test_data/execution_requests/{deploy_process.id}.json")) execute_json = json.load(f) execute = Execute.model_validate(execute_json) response = client.post(f"/processes/{deploy_process.id}/execution", json=jsonable_encoder(execute)) @@ -79,7 +86,7 @@ def test_post_execute_process(test_directory, client, deploy_process): def test_delete_dismiss_execution(test_directory, client, deploy_process): - data_filename = os.path.join(test_directory, "test_data/execution_requests/execute_cwltool_help_dag.json") + data_filename = os.path.join(test_directory, f"test_data/execution_requests/{deploy_process.id}.json") f = open(data_filename) execute_json = json.load(f) execute = Execute.model_validate(execute_json) @@ -131,41 +138,40 @@ def test_get_results(client, execute_process): assert Results.model_validate(data) -@pytest.mark.dependency() -def test_post_deploy_process_cwl_dag(test_directory, client): - data_filename = os.path.join(test_directory, "test_data/process_descriptions/cwl_dag_process.json") - f = open(data_filename) +@pytest.mark.parametrize("process_filename", PROCESS_FILES) +def test_post_deploy_process_dag(test_directory, client, process_filename): + f = open(process_filename) process_json = json.load(f) process = Process.model_validate(process_json) response = client.post("/processes", json=process.model_dump()) assert response.status_code == status.HTTP_200_OK data = response.json() process = Process.model_validate(data) - assert process.id == "cwl_dag" - assert process.title == "CWL DAG Process" + assert process.id == pathlib.Path(process_filename).stem -@pytest.mark.dependency(depends=["test_post_deploy_process_cwl_dag"]) -def test_post_execute_process_cwl_dag(test_directory, client): - data_filename = os.path.join(test_directory, "test_data/execution_requests/cwl_dag_request.json") - f = open(data_filename) +@pytest.mark.dependency(name="test_post_execute_process_dag") +@pytest.mark.parametrize("execution_filename", EXECUTION_FILES) +def test_post_execute_process_dag(test_directory, client, execution_filename): + f = open(execution_filename) execute_json = json.load(f) execute = Execute.model_validate(execute_json) - response = client.post("/processes/cwl_dag/execution", json=jsonable_encoder(execute)) + process_id = pathlib.Path(execution_filename).stem + response = client.post(f"/processes/{process_id}/execution", json=jsonable_encoder(execute)) assert response.status_code == status.HTTP_200_OK data = response.json() assert StatusInfo.model_validate(data) - assert data["processID"] == "cwl_dag" + assert data["processID"] == process_id assert data["type"] == "process" -@pytest.mark.dependency(depends=["test_post_execute_process_cwl_dag"]) -def test_get_status_cwl_dag(test_directory, client): - data_filename = os.path.join(test_directory, "test_data/execution_requests/cwl_dag_request.json") - f = open(data_filename) +@pytest.mark.parametrize("execution_filename", EXECUTION_FILES) +def test_get_status_dag(test_directory, client, execution_filename): + f = open(execution_filename) execute_json = json.load(f) execute = Execute.model_validate(execute_json) - execute_response = client.post("/processes/cwl_dag/execution", json=jsonable_encoder(execute)) + process_id = pathlib.Path(execution_filename).stem + execute_response = client.post(f"/processes/{process_id}/execution", json=jsonable_encoder(execute)) data = execute_response.json() status_info = StatusInfo.model_validate(data) job_id = status_info.jobID @@ -181,9 +187,11 @@ def test_get_status_cwl_dag(test_directory, client): assert status_info.jobID == job_id -@pytest.mark.dependency(depends=["test_get_status_cwl_dag"]) -def test_delete_undeploy_cwl_dag(client): - response = client.delete("/processes/cwl_dag") +@pytest.mark.parametrize("process_filename", PROCESS_FILES) +@pytest.mark.dependency(depends=["test_post_execute_process_dag"]) +def test_delete_undeploy_dag(client, process_filename): + process_id = pathlib.Path(process_filename).stem + response = client.delete(f"/processes/{process_id}") assert response.status_code == status.HTTP_409_CONFLICT - response = client.delete("/processes/cwl_dag", params={"force": True}) + response = client.delete(f"/processes/{process_id}", params={"force": True}) assert response.status_code == status.HTTP_204_NO_CONTENT From b252499538b723128873c73e830901710e27f3e4 Mon Sep 17 00:00:00 2001 From: Nikki <17799906+nikki-t@users.noreply.github.com> Date: Fri, 5 Jul 2024 08:08:28 -0400 Subject: [PATCH 04/60] Fix JSON file formatting --- .../execution_requests/EchoProcess.json | 221 +++++++++--------- 1 file changed, 110 insertions(+), 111 deletions(-) diff --git a/unity-test/test_data/execution_requests/EchoProcess.json b/unity-test/test_data/execution_requests/EchoProcess.json index 65ce3dd..a57656b 100644 --- a/unity-test/test_data/execution_requests/EchoProcess.json +++ b/unity-test/test_data/execution_requests/EchoProcess.json @@ -1,121 +1,120 @@ { - "inputs": { - "arrayInput": [ - 1, - 2, - 3, - 4, - 5, - 6 + "inputs": { + "arrayInput": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "boundingBoxInput": { + "bbox": [ + 51.9, + 7, + 52, + 7.1 ], - "boundingBoxInput": { - "bbox": [ - 51.9, - 7, - 52, - 7.1 - ], - "crs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84" - }, - "complexObjectInput": { - "value": { - "property1": "value1", - "property2": "value2", - "property5": true - } - }, - "dateInput": "2021-03-06T07:21:00", - "doubleInput": 3.14159, - "featureCollectionInput": { + "crs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + }, + "complexObjectInput": { + "value": { + "property1": "value1", + "property2": "value2", + "property5": true + } + }, + "dateInput": "2021-03-06T07:21:00", + "doubleInput": 3.14159, + "featureCollectionInput": { + "mediaType": "application/gml+xml; version=3.2", + "value": "..." + }, + "geometryInput": [ + { "mediaType": "application/gml+xml; version=3.2", - "value": "..." + "value": "-77.024519 38.810529 -77.024635 38.810973 -77.024704 38.810962 -77.024776 38.811239 -77.024957 38.81121 -77.024905 38.811012 -77.024905 38.811012 -77.024865 38.810857 -77.025024 38.810832 -77.025071 38.811012 -77.025203 38.810992 -77.02506 38.810444 -77.024519 38.810529" }, - "geometryInput": [ - { - "mediaType": "application/gml+xml; version=3.2", - "value": "-77.024519 38.810529 -77.024635 38.810973 -77.024704 38.810962 -77.024776 38.811239 -77.024957 38.81121 -77.024905 38.811012 -77.024905 38.811012 -77.024865 38.810857 -77.025024 38.810832 -77.025071 38.811012 -77.025203 38.810992 -77.02506 38.810444 -77.024519 38.810529" - }, - { - "value": { - "coordinates": [ + { + "value": { + "coordinates": [ + [ + [ + -176.5814819, + -44.10896301 + ], + [ + -176.5818024, + -44.10964584 + ], + [ + -176.5844116, + -44.11236572 + ], + [ + -176.5935974, + -44.11021805 + ], [ - [ - -176.5814819, - -44.10896301 - ], - [ - -176.5818024, - -44.10964584 - ], - [ - -176.5844116, - -44.11236572 - ], - [ - -176.5935974, - -44.11021805 - ], - [ - -176.5973511, - -44.10743332 - ], - [ - -176.5950928, - -44.10562134 - ], - [ - -176.5858459, - -44.1043396 - ], - [ - -176.5811157, - -44.10667801 - ], - [ - -176.5814819, - -44.10896301 - ] + -176.5973511, + -44.10743332 + ], + [ + -176.5950928, + -44.10562134 + ], + [ + -176.5858459, + -44.1043396 + ], + [ + -176.5811157, + -44.10667801 + ], + [ + -176.5814819, + -44.10896301 ] - ], - "type": "Polygon" - } - } - ], - "imagesInput": [ - { - "href": "https://www.someserver.com/ogcapi/Daraa/collections/Daraa_DTED/styles/Topographic/coverage?...", - "type": "image/tiff; application=geotiff" - }, - { - "encoding": "base64", - "mediaType": "image/jp2", - "value": "VBORw0KGgoAAAANSUhEUgAABvwAAAa4CAYAAABMB35kAAABhGlDQ1BJQ0MgcHJvZmlsZQAA\nKJF9kT1Iw0AcxV9TpSL1A+xQxCFDdbIgKuKoVShChVArtOpgcumH0KQhSXFxFFwLDn4sVh1c\nnHV1cBUEwQ8QNzcnRRcp8X9JoUWMB8f9eHfvcfcOEOplplkdY4Cm22Y6mRCzuRUx9IogouhH\n ... \nj3Z5mX7/PCPVRJV92rpHK24xcJrzk20+tkeYlCPqcZNO3Lpni1OJWatPCcmgGDEqx7Om6lfa\nppM4k4BTe9+bsn3L9/9/yWhA0PwQGW8ipCZsnZt9lsdrYEM8z/M8z/M8z/M8z/M8z/MzLWY1\nAAAACUlEQVQ871H6P6JI+TxS5Wn2AAAAAElFTkSuQmCC" - } - ], - "measureInput": { - "value": { - "measurement": 10.3, - "reference": "https://ucum.org/ucum-essence.xml", - "uom": "m" + ] + ], + "type": "Polygon" } + } + ], + "imagesInput": [ + { + "href": "https://www.someserver.com/ogcapi/Daraa/collections/Daraa_DTED/styles/Topographic/coverage?...", + "type": "image/tiff; application=geotiff" }, - "stringInput": "Value2" + { + "encoding": "base64", + "mediaType": "image/jp2", + "value": "VBORw0KGgoAAAANSUhEUgAABvwAAAa4CAYAAABMB35kAAABhGlDQ1BJQ0MgcHJvZmlsZQAA\nKJF9kT1Iw0AcxV9TpSL1A+xQxCFDdbIgKuKoVShChVArtOpgcumH0KQhSXFxFFwLDn4sVh1c\nnHV1cBUEwQ8QNzcnRRcp8X9JoUWMB8f9eHfvcfcOEOplplkdY4Cm22Y6mRCzuRUx9IogouhH\n ... \nj3Z5mX7/PCPVRJV92rpHK24xcJrzk20+tkeYlCPqcZNO3Lpni1OJWatPCcmgGDEqx7Om6lfa\nppM4k4BTe9+bsn3L9/9/yWhA0PwQGW8ipCZsnZt9lsdrYEM8z/M8z/M8z/M8z/M8z/MzLWY1\nAAAACUlEQVQ871H6P6JI+TxS5Wn2AAAAAElFTkSuQmCC" + } + ], + "measureInput": { + "value": { + "measurement": 10.3, + "reference": "https://ucum.org/ucum-essence.xml", + "uom": "m" + } }, - "outputs": { - "arrayOutput": {}, - "boundingBoxOutput": {}, - "complexObjectOutput": {}, - "dateOutput": {}, - "doubleOutput": {}, - "featureCollectionOutput": {}, - "geometryOutput": {}, - "imageOutput": { - "format": { - "mediaType": "image/tiff; application=geotiff" - } - }, - "measureOutput": {}, - "stringOutput": {} - } + "stringInput": "Value2" + }, + "outputs": { + "arrayOutput": {}, + "boundingBoxOutput": {}, + "complexObjectOutput": {}, + "dateOutput": {}, + "doubleOutput": {}, + "featureCollectionOutput": {}, + "geometryOutput": {}, + "imageOutput": { + "format": { + "mediaType": "image/tiff; application=geotiff" + } + }, + "measureOutput": {}, + "stringOutput": {} } - \ No newline at end of file +} From 2f0f3e7ca5b76ff1f83f92007e4f7d0527641116 Mon Sep 17 00:00:00 2001 From: Nikki <17799906+nikki-t@users.noreply.github.com> Date: Fri, 5 Jul 2024 13:33:10 -0400 Subject: [PATCH 05/60] Allow execution of workflow via dispatch and on pull request --- .github/workflows/unit_tests.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index ee1c430..6d4da32 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -1,6 +1,8 @@ name: Unit Tests -on: [pull_request] +on: + pull_request: + workflow_dispatch: jobs: unit-tests: From 789cd10860f5ab9411905d004bd27f281b1143cd Mon Sep 17 00:00:00 2001 From: Nikki <17799906+nikki-t@users.noreply.github.com> Date: Fri, 5 Jul 2024 13:35:10 -0400 Subject: [PATCH 06/60] Fix trailing whitespace --- .github/workflows/unit_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 6d4da32..9ce886e 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -1,6 +1,6 @@ name: Unit Tests -on: +on: pull_request: workflow_dispatch: From 752dea03224f3d2a8ca6fa7a9a79efb029726543 Mon Sep 17 00:00:00 2001 From: Nikki <17799906+nikki-t@users.noreply.github.com> Date: Fri, 5 Jul 2024 13:36:52 -0400 Subject: [PATCH 07/60] Remove duplicate unit tests --- unity-test/test_main.py | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/unity-test/test_main.py b/unity-test/test_main.py index 101a7ed..a671043 100644 --- a/unity-test/test_main.py +++ b/unity-test/test_main.py @@ -52,39 +52,6 @@ def test_get_conformance_declaration(client): ] -@pytest.mark.parametrize("process_filename", PROCESS_FILES) -@pytest.mark.dependency(name="test_post_deploy_process") -def test_post_deploy_process(client, process_filename): - f = open(process_filename) - process_json = json.load(f) - process = Process.model_validate(process_json) - response = client.post("/processes", json=process.model_dump()) - assert response.status_code == status.HTTP_200_OK - data = response.json() - process = Process.model_validate(data) - assert process.id == pathlib.Path(process_filename).stem - - -@pytest.mark.parametrize("process_filename", PROCESS_FILES) -@pytest.mark.dependency(depends=["test_post_deploy_process"]) -def test_delete_undeploy_process(client, process_filename): - process_id = pathlib.Path(process_filename).stem - response = client.delete(f"/processes/{process_id}") - assert response.status_code == status.HTTP_409_CONFLICT - response = client.delete(f"/processes/{process_id}", params={"force": True}) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -def test_post_execute_process(test_directory, client, deploy_process): - f = open(os.path.join(test_directory, f"test_data/execution_requests/{deploy_process.id}.json")) - execute_json = json.load(f) - execute = Execute.model_validate(execute_json) - response = client.post(f"/processes/{deploy_process.id}/execution", json=jsonable_encoder(execute)) - assert response.status_code == status.HTTP_200_OK - data = response.json() - assert StatusInfo.model_validate(data) - - def test_delete_dismiss_execution(test_directory, client, deploy_process): data_filename = os.path.join(test_directory, f"test_data/execution_requests/{deploy_process.id}.json") f = open(data_filename) From 5a6e628f8959231ed380aa1bb6f495569095c93b Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Wed, 14 Aug 2024 11:15:43 -0700 Subject: [PATCH 08/60] feat: Initial autogenerated FastAPI implementation --- app-cp/.flake8 | 3 + app-cp/.gitignore | 138 + app-cp/.openapi-generator-ignore | 23 + app-cp/.openapi-generator/FILES | 130 + app-cp/.openapi-generator/VERSION | 1 + app-cp/Dockerfile | 30 + app-cp/README.md | 39 + app-cp/docker-compose.yaml | 9 + app-cp/openapi.yaml | 3441 +++++++++++++++++ app-cp/pyproject.toml | 30 + app-cp/requirements.txt | 36 + app-cp/setup.cfg | 20 + app-cp/src/openapi_server/impl/__init__.py | 0 .../apis/__init__.py | 0 .../apis/api_api.py | 82 + .../apis/api_api_base.py | 23 + .../apis/conformance_api.py | 56 + .../apis/conformance_api_base.py | 18 + .../apis/dru_api.py | 111 + .../apis/dru_api_base.py | 36 + .../apis/jobs_api.py | 121 + .../apis/jobs_api_base.py | 43 + .../apis/landing_page_api.py | 56 + .../apis/landing_page_api_base.py | 18 + .../apis/processes_api.py | 118 + .../apis/processes_api_base.py | 39 + .../src/unity_sps_ogc_processes_api/main.py | 40 + .../models/__init__.py | 0 .../models/bbox.py | 101 + .../models/bbox1.py | 101 + .../models/bbox_def_crs.py | 158 + .../models/bbox_processes.py | 101 + .../models/collection_info.py | 199 + .../models/collection_info_data_type.py | 85 + .../models/collections.py | 138 + .../models/conf_classes.py | 86 + .../unity_sps_ogc_processes_api/models/crs.py | 168 + .../models/crs_one_of.py | 186 + .../models/crs_one_of_one_of.py | 88 + .../models/crs_one_of_one_of1.py | 86 + .../models/crs_one_of_one_of2.py | 89 + .../models/data_type.py | 167 + .../models/description_type.py | 109 + .../models/enumeration.py | 94 + .../models/exception.py | 118 + .../models/execute.py | 133 + .../models/execute200_response.py | 310 ++ .../models/execute200_response1.py | 124 + .../models/execute_workflows.py | 162 + .../models/execute_workflows1.py | 162 + .../models/execution_unit.py | 142 + .../models/execution_unit_config.py | 158 + .../models/extent.py | 109 + .../models/extent_spatial.py | 148 + .../models/extent_spatial_grid_inner.py | 132 + ...nt_spatial_grid_inner_coordinates_inner.py | 174 + .../extent_spatial_grid_inner_resolution.py | 174 + .../models/extent_temporal.py | 134 + .../models/extent_temporal_grid.py | 115 + .../models/extent_temporal_grid_resolution.py | 174 + .../models/extent_uad.py | 109 + .../models/extra_models.py | 9 + .../models/feature_collection.py | 124 + .../models/fields_modifiers.py | 105 + .../models/fields_modifiers_properties.py | 167 + .../models/format.py | 103 + .../models/format_schema.py | 167 + .../models/geo_json_feature.py | 139 + .../models/geo_json_feature_geometry.py | 257 ++ .../models/geo_json_feature_id.py | 174 + .../models/geo_json_line_string.py | 114 + .../models/geo_json_multi_line_string.py | 116 + .../models/geo_json_multi_point.py | 113 + .../models/geo_json_multi_polygon.py | 120 + .../models/geo_json_point.py | 111 + .../models/geo_json_polygon.py | 116 + .../models/inline_or_ref_data.py | 184 + .../models/inline_or_ref_data1.py | 192 + .../models/inline_or_ref_data_workflows.py | 197 + .../models/input.py | 167 + .../models/input_collection.py | 107 + .../models/input_description.py | 161 + .../input_description_all_of_max_occurs.py | 167 + .../models/input_parameterized.py | 107 + .../models/input_process.py | 166 + .../models/input_value.py | 155 + .../models/input_value1.py | 157 + .../models/input_value_no_object.py | 268 ++ .../models/input_value_no_object1.py | 268 ++ .../models/input_value_no_object_workflows.py | 344 ++ .../models/input_value_workflows.py | 173 + .../models/input_workflows.py | 176 + .../models/input_workflows1.py | 180 + .../models/job_control_options.py | 43 + .../models/job_list.py | 117 + .../models/landing_page.py | 112 + .../models/link.py | 98 + .../models/metadata.py | 166 + .../models/metadata_one_of.py | 107 + .../models/metadata_one_of1.py | 107 + .../models/metadata_one_of1_value.py | 167 + .../models/model_schema.py | 162 + .../models/ogcapppkg.py | 113 + .../models/ogcapppkg_array_inner.py | 180 + .../models/ogcapppkg_execution_unit.py | 207 + .../models/output.py | 99 + .../models/output_description.py | 125 + .../models/output_workflows.py | 101 + .../models/output_workflows1.py | 101 + .../models/process.py | 177 + .../models/process_list.py | 117 + .../models/process_summary.py | 141 + .../models/processes_list.py | 43 + .../models/qualified_input_value.py | 113 + .../models/qualified_input_value1.py | 113 + .../models/qualified_input_value_workflows.py | 142 + .../models/reference.py | 86 + .../models/schema1.py | 167 + .../models/schema_one_of.py | 349 ++ .../schema_one_of_additional_properties.py | 169 + .../models/static_indicator.py | 146 + .../models/status_code.py | 45 + .../models/status_info.py | 156 + .../models/subscriber.py | 94 + .../security_api.py | 20 + app-cp/tests/conftest.py | 17 + app-cp/tests/test_api_api.py | 40 + app-cp/tests/test_conformance_api.py | 23 + app-cp/tests/test_dru_api.py | 64 + app-cp/tests/test_jobs_api.py | 78 + app-cp/tests/test_landing_page_api.py | 23 + app-cp/tests/test_processes_api.py | 73 + openapi-generator-config.yaml | 2 + 133 files changed, 18702 insertions(+) create mode 100644 app-cp/.flake8 create mode 100644 app-cp/.gitignore create mode 100644 app-cp/.openapi-generator-ignore create mode 100644 app-cp/.openapi-generator/FILES create mode 100644 app-cp/.openapi-generator/VERSION create mode 100644 app-cp/Dockerfile create mode 100644 app-cp/README.md create mode 100644 app-cp/docker-compose.yaml create mode 100644 app-cp/openapi.yaml create mode 100644 app-cp/pyproject.toml create mode 100644 app-cp/requirements.txt create mode 100644 app-cp/setup.cfg create mode 100644 app-cp/src/openapi_server/impl/__init__.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/apis/__init__.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/apis/api_api.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/apis/api_api_base.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/apis/dru_api_base.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api_base.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/apis/processes_api.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/apis/processes_api_base.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/main.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/__init__.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/bbox.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/bbox1.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/bbox_processes.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/collection_info.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/collection_info_data_type.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/collections.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/conf_classes.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/crs.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of1.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of2.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/data_type.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/description_type.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/enumeration.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/exception.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/execute.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/execute200_response.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/execute200_response1.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/execute_workflows.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/execute_workflows1.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/execution_unit.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/execution_unit_config.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/extent.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/extent_uad.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/extra_models.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/feature_collection.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/format.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/format_schema.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/geo_json_point.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/input.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/input_collection.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/input_description.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/input_parameterized.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/input_process.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/input_value.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/input_value1.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/input_value_workflows.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/input_workflows.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/input_workflows1.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/job_control_options.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/job_list.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/landing_page.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/link.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/metadata.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/model_schema.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/output.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/output_description.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/output_workflows.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/output_workflows1.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/process.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/process_list.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/process_summary.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/processes_list.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/reference.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/schema1.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/schema_one_of.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/static_indicator.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/status_code.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/status_info.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/models/subscriber.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/security_api.py create mode 100644 app-cp/tests/conftest.py create mode 100644 app-cp/tests/test_api_api.py create mode 100644 app-cp/tests/test_conformance_api.py create mode 100644 app-cp/tests/test_dru_api.py create mode 100644 app-cp/tests/test_jobs_api.py create mode 100644 app-cp/tests/test_landing_page_api.py create mode 100644 app-cp/tests/test_processes_api.py create mode 100644 openapi-generator-config.yaml diff --git a/app-cp/.flake8 b/app-cp/.flake8 new file mode 100644 index 0000000..9e008c5 --- /dev/null +++ b/app-cp/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 88 +exclude = .git,__pycache__,__init__.py,.mypy_cache,.pytest_cache,.venv diff --git a/app-cp/.gitignore b/app-cp/.gitignore new file mode 100644 index 0000000..a81c8ee --- /dev/null +++ b/app-cp/.gitignore @@ -0,0 +1,138 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ diff --git a/app-cp/.openapi-generator-ignore b/app-cp/.openapi-generator-ignore new file mode 100644 index 0000000..7484ee5 --- /dev/null +++ b/app-cp/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/app-cp/.openapi-generator/FILES b/app-cp/.openapi-generator/FILES new file mode 100644 index 0000000..d1ee7ca --- /dev/null +++ b/app-cp/.openapi-generator/FILES @@ -0,0 +1,130 @@ +.flake8 +.gitignore +.openapi-generator-ignore +Dockerfile +README.md +docker-compose.yaml +openapi.yaml +pyproject.toml +requirements.txt +setup.cfg +src/openapi_server/impl/__init__.py +src/unity_sps_ogc_processes_api/apis/__init__.py +src/unity_sps_ogc_processes_api/apis/api_api.py +src/unity_sps_ogc_processes_api/apis/api_api_base.py +src/unity_sps_ogc_processes_api/apis/conformance_api.py +src/unity_sps_ogc_processes_api/apis/conformance_api_base.py +src/unity_sps_ogc_processes_api/apis/dru_api.py +src/unity_sps_ogc_processes_api/apis/dru_api_base.py +src/unity_sps_ogc_processes_api/apis/jobs_api.py +src/unity_sps_ogc_processes_api/apis/jobs_api_base.py +src/unity_sps_ogc_processes_api/apis/landing_page_api.py +src/unity_sps_ogc_processes_api/apis/landing_page_api_base.py +src/unity_sps_ogc_processes_api/apis/processes_api.py +src/unity_sps_ogc_processes_api/apis/processes_api_base.py +src/unity_sps_ogc_processes_api/main.py +src/unity_sps_ogc_processes_api/models/__init__.py +src/unity_sps_ogc_processes_api/models/bbox.py +src/unity_sps_ogc_processes_api/models/bbox1.py +src/unity_sps_ogc_processes_api/models/bbox_def_crs.py +src/unity_sps_ogc_processes_api/models/bbox_processes.py +src/unity_sps_ogc_processes_api/models/collection_info.py +src/unity_sps_ogc_processes_api/models/collection_info_data_type.py +src/unity_sps_ogc_processes_api/models/collections.py +src/unity_sps_ogc_processes_api/models/conf_classes.py +src/unity_sps_ogc_processes_api/models/crs.py +src/unity_sps_ogc_processes_api/models/crs_one_of.py +src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py +src/unity_sps_ogc_processes_api/models/crs_one_of_one_of1.py +src/unity_sps_ogc_processes_api/models/crs_one_of_one_of2.py +src/unity_sps_ogc_processes_api/models/data_type.py +src/unity_sps_ogc_processes_api/models/description_type.py +src/unity_sps_ogc_processes_api/models/enumeration.py +src/unity_sps_ogc_processes_api/models/exception.py +src/unity_sps_ogc_processes_api/models/execute.py +src/unity_sps_ogc_processes_api/models/execute200_response.py +src/unity_sps_ogc_processes_api/models/execute200_response1.py +src/unity_sps_ogc_processes_api/models/execute_workflows.py +src/unity_sps_ogc_processes_api/models/execute_workflows1.py +src/unity_sps_ogc_processes_api/models/execution_unit.py +src/unity_sps_ogc_processes_api/models/execution_unit_config.py +src/unity_sps_ogc_processes_api/models/extent.py +src/unity_sps_ogc_processes_api/models/extent_spatial.py +src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py +src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py +src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py +src/unity_sps_ogc_processes_api/models/extent_temporal.py +src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py +src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py +src/unity_sps_ogc_processes_api/models/extent_uad.py +src/unity_sps_ogc_processes_api/models/extra_models.py +src/unity_sps_ogc_processes_api/models/feature_collection.py +src/unity_sps_ogc_processes_api/models/fields_modifiers.py +src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py +src/unity_sps_ogc_processes_api/models/format.py +src/unity_sps_ogc_processes_api/models/format_schema.py +src/unity_sps_ogc_processes_api/models/geo_json_feature.py +src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py +src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py +src/unity_sps_ogc_processes_api/models/geo_json_line_string.py +src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py +src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py +src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py +src/unity_sps_ogc_processes_api/models/geo_json_point.py +src/unity_sps_ogc_processes_api/models/geo_json_polygon.py +src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py +src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py +src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py +src/unity_sps_ogc_processes_api/models/input.py +src/unity_sps_ogc_processes_api/models/input_collection.py +src/unity_sps_ogc_processes_api/models/input_description.py +src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py +src/unity_sps_ogc_processes_api/models/input_parameterized.py +src/unity_sps_ogc_processes_api/models/input_process.py +src/unity_sps_ogc_processes_api/models/input_value.py +src/unity_sps_ogc_processes_api/models/input_value1.py +src/unity_sps_ogc_processes_api/models/input_value_no_object.py +src/unity_sps_ogc_processes_api/models/input_value_no_object1.py +src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py +src/unity_sps_ogc_processes_api/models/input_value_workflows.py +src/unity_sps_ogc_processes_api/models/input_workflows.py +src/unity_sps_ogc_processes_api/models/input_workflows1.py +src/unity_sps_ogc_processes_api/models/job_control_options.py +src/unity_sps_ogc_processes_api/models/job_list.py +src/unity_sps_ogc_processes_api/models/landing_page.py +src/unity_sps_ogc_processes_api/models/link.py +src/unity_sps_ogc_processes_api/models/metadata.py +src/unity_sps_ogc_processes_api/models/metadata_one_of.py +src/unity_sps_ogc_processes_api/models/metadata_one_of1.py +src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py +src/unity_sps_ogc_processes_api/models/model_schema.py +src/unity_sps_ogc_processes_api/models/ogcapppkg.py +src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py +src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py +src/unity_sps_ogc_processes_api/models/output.py +src/unity_sps_ogc_processes_api/models/output_description.py +src/unity_sps_ogc_processes_api/models/output_workflows.py +src/unity_sps_ogc_processes_api/models/output_workflows1.py +src/unity_sps_ogc_processes_api/models/process.py +src/unity_sps_ogc_processes_api/models/process_list.py +src/unity_sps_ogc_processes_api/models/process_summary.py +src/unity_sps_ogc_processes_api/models/processes_list.py +src/unity_sps_ogc_processes_api/models/qualified_input_value.py +src/unity_sps_ogc_processes_api/models/qualified_input_value1.py +src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py +src/unity_sps_ogc_processes_api/models/reference.py +src/unity_sps_ogc_processes_api/models/schema1.py +src/unity_sps_ogc_processes_api/models/schema_one_of.py +src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py +src/unity_sps_ogc_processes_api/models/static_indicator.py +src/unity_sps_ogc_processes_api/models/status_code.py +src/unity_sps_ogc_processes_api/models/status_info.py +src/unity_sps_ogc_processes_api/models/subscriber.py +src/unity_sps_ogc_processes_api/security_api.py +tests/conftest.py +tests/test_api_api.py +tests/test_conformance_api.py +tests/test_dru_api.py +tests/test_jobs_api.py +tests/test_landing_page_api.py +tests/test_processes_api.py diff --git a/app-cp/.openapi-generator/VERSION b/app-cp/.openapi-generator/VERSION new file mode 100644 index 0000000..1985849 --- /dev/null +++ b/app-cp/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.7.0 diff --git a/app-cp/Dockerfile b/app-cp/Dockerfile new file mode 100644 index 0000000..b66458e --- /dev/null +++ b/app-cp/Dockerfile @@ -0,0 +1,30 @@ +FROM python:3.7 AS builder + +WORKDIR /usr/src/app + +RUN python3 -m venv /venv +ENV PATH="/venv/bin:$PATH" + +RUN pip install --upgrade pip + +COPY . . +RUN pip install --no-cache-dir . + + +FROM python:3.7 AS test_runner +WORKDIR /tmp +COPY --from=builder /venv /venv +COPY --from=builder /usr/src/app/tests tests +ENV PATH=/venv/bin:$PATH + +# install test dependencies +RUN pip install pytest + +# run tests +RUN pytest tests + + +FROM python:3.7 AS service +WORKDIR /root/app/site-packages +COPY --from=test_runner /venv /venv +ENV PATH=/venv/bin:$PATH diff --git a/app-cp/README.md b/app-cp/README.md new file mode 100644 index 0000000..a88a3ba --- /dev/null +++ b/app-cp/README.md @@ -0,0 +1,39 @@ +# OpenAPI generated FastAPI server + +This Python package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: + +- API version: 0.1 +- Generator version: 7.7.0 +- Build package: org.openapitools.codegen.languages.PythonFastAPIServerCodegen + +## Requirements. + +Python >= 3.7 + +## Installation & Usage + +To run the server, please execute the following from the root directory: + +```bash +pip3 install -r requirements.txt +PYTHONPATH=src uvicorn openapi_server.main:app --host 0.0.0.0 --port 8080 +``` + +and open your browser at `http://localhost:8080/docs/` to see the docs. + +## Running with Docker + +To run the server on a Docker container, please execute the following from the root directory: + +```bash +docker-compose up --build +``` + +## Tests + +To run the tests: + +```bash +pip3 install pytest +PYTHONPATH=src pytest tests +``` diff --git a/app-cp/docker-compose.yaml b/app-cp/docker-compose.yaml new file mode 100644 index 0000000..cf3a2ef --- /dev/null +++ b/app-cp/docker-compose.yaml @@ -0,0 +1,9 @@ +version: '3.6' +services: + service: + build: + context: . + target: service + ports: + - "8080:8080" + command: uvicorn unity_sps_ogc_processes_api.main:app --host 0.0.0.0 --port 8080 diff --git a/app-cp/openapi.yaml b/app-cp/openapi.yaml new file mode 100644 index 0000000..b2efd19 --- /dev/null +++ b/app-cp/openapi.yaml @@ -0,0 +1,3441 @@ +openapi: 3.0.0 +info: + contact: + email: info@ogc.org + name: Open Geospatial Consortium + description: Example API Definition for OGC API - Processes + license: + name: OGC License + url: http://www.opengeospatial.org/legal/ + title: OGC API - Processes + version: "0.1" +servers: +- description: "Example Server 1 (GNOSIS, supporting Part 1: Core /Sync and Part 3:\ + \ Workflows)" + url: https://maps.gnosis.earth/ogcapi +paths: + /: + get: + operationId: getLandingPage + parameters: + - description: "The format of the response. If no value is provided, the accept\ + \ header is used to determine the format. Accepted values are 'json' or\ + \ 'html'." + explode: false + in: query + name: f + required: false + schema: + enum: + - json + - html + type: string + style: form + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/landingPage' + text/html: + schema: + type: string + description: |- + The landing page provides links to the API definition (link relation `service-desc`, in this case path `/api`), + to the Conformance declaration (path `/conformance`, link relation `http://www.opengis.net/def/rel/ogc/1.0/conformance`), and to other resources. + "406": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: "Content negotiation failed. For example, the `Accept` header\ + \ submitted in the request did not support any of the media types supported\ + \ by the server for the requested resource." + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: A server error occurred. + summary: Retrieve the OGC API landing page for this service. + tags: + - Landing Page + /conformance: + get: + operationId: getConformance + parameters: + - description: "The format of the response. If no value is provided, the accept\ + \ header is used to determine the format. Accepted values are 'json' or\ + \ 'html'." + explode: false + in: query + name: f + required: false + schema: + enum: + - json + - html + type: string + style: form + responses: + "200": + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/confClasses' + example: + conformsTo: + - http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/core + - http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/json + - http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/html + - http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/oas30 + - http://www.opengis.net/spec/ogcapi-common-2/1.0/conf/collections + - http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/core + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/nested-processes + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/remote-core-processes + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/collection-input + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/remote-collections + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/input-fields-modifiers + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/output-fields-modifiers + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/deployable-workflows + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/collection-output + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/cwl-workflows + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/openeo-workflows + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/core + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tileset + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tilesets-list + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geodata-tilesets + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/dataset-tilesets + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geodata-selection + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/jpeg + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/png + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/mvt + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geojson + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tiff + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/netcdf + description: |- + The URIs of all conformance classes supported by the server + + To support "generic" clients that want to access multiple + OGC API - Processes implementations - and not "just" a specific + API / server, the server declares the conformance + classes it implements and conforms to. + "406": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: "Content negotiation failed. For example, the `Accept` header\ + \ submitted in the request did not support any of the media types supported\ + \ by the server for the requested resource." + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: A server error occurred. + summary: Retrieve the set of OGC API conformance classes that are supported + by this service. + tags: + - Conformance + /api: + get: + operationId: getAPI + parameters: + - description: "The format of the response. If no value is provided, the accept\ + \ header is used to determine the format. Accepted values are 'json' or\ + \ 'html'." + explode: false + in: query + name: f + required: false + schema: + enum: + - json + - html + type: string + style: form + responses: + "200": + content: + application/vnd.oai.openapi+json;version=3.0: + schema: + type: object + text/html: + schema: + type: string + description: The OpenAPI definition of the API. + "406": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: "Content negotiation failed. For example, the `Accept` header\ + \ submitted in the request did not support any of the media types supported\ + \ by the server for the requested resource." + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: A server error occurred. + summary: Retrieve this API definition. + tags: + - API + /api/processes-list: + get: + operationId: getAPIProcesses + parameters: + - description: "The format of the response. If no value is provided, the accept\ + \ header is used to determine the format. Accepted values are 'json' or\ + \ 'html'." + explode: false + in: query + name: f + required: false + schema: + enum: + - json + - html + type: string + style: form + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/enumeration' + text/html: + schema: + type: string + description: An enumerated list of valid string values for API parameters. + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: "The requested resource does not exist on the server. For example,\ + \ a path parameter had an incorrect value." + "406": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: "Content negotiation failed. For example, the `Accept` header\ + \ submitted in the request did not support any of the media types supported\ + \ by the server for the requested resource." + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: A server error occurred. + summary: Retrieve the list of processes available from this API implementation + & deployment. + tags: + - API + /processes: + get: + description: | + The list of processes contains a summary of each process the OGC API - Processes offers, including the link to a more detailed description of the process. + + For more information, see [Section 7.7]https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_process_list). + operationId: getProcesses + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/processList' + description: Information about the available processes + summary: retrieve the list of available processes + tags: + - Processes + post: + description: | + Deploys a process. + + For more information, see [Section 6.3](http://docs.ogc.org/DRAFTS/20-044.html#_87a6983e-d060-458c-95ab-27e232e64822). + operationId: deploy + parameters: + - description: Point to the workflow identifier for deploying a CWL containing + multiple workflow definitions + in: query + name: w + required: false + schema: + type: string + requestBody: + content: + application/ogcapppkg+json: + schema: + $ref: '#/components/schemas/ogcapppkg' + application/cwl: + schema: + $ref: '#/components/schemas/cwl' + application/cwl+json: + schema: + $ref: '#/components/schemas/cwl' + application/cwl+yaml: + schema: + $ref: '#/components/schemas/cwl' + description: An OGC Application Package used to deploy a new process. + required: true + responses: + "201": {} + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: the processes is not mutable + "409": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: the processes being added is already deployed (i.e. duplicate) + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: A server error occurred. + summary: deploy a process. + tags: + - DRU + /processes/{processId}: + delete: + description: | + Undeploys a process. + + For more information, see [Section 6.5](http://docs.ogc.org/DRAFTS/20-044.html#_16391f9e-538f-4a84-9710-72a6bab82842). + operationId: undeploy + parameters: + - in: path + name: processId + required: true + schema: + $ref: '#/components/schemas/processes-list' + responses: + "204": + description: successful operation (no response body) + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: the processes is not mutable + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: "The requested resource does not exist on the server. For example,\ + \ a path parameter had an incorrect value." + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: A server error occurred. + summary: undeploy a process. + tags: + - DRU + get: + description: | + The process description contains information about inputs and outputs and a link to the execution-endpoint for the process. The Core does not mandate the use of a specific process description to specify the interface of a process. That said, the Core requirements class makes the following recommendation: + + Implementations SHOULD consider supporting the OGC process description. + + For more information, see [Section 7.8](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_process_description). + operationId: getProcessDescription + parameters: + - in: path + name: processId + required: true + schema: + $ref: '#/components/schemas/processes-list' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/process' + description: A process description. + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: "The requested resource does not exist on the server. For example,\ + \ a path parameter had an incorrect value." + summary: retrieve a process description + tags: + - Processes + put: + description: | + Replaces a process. + + For more information, see [Section 6.4](http://docs.ogc.org/DRAFTS/20-044.html#_18582f42-ebc6-4284-9333-c089068f62b6). + operationId: replace + parameters: + - in: path + name: processId + required: true + schema: + $ref: '#/components/schemas/processes-list' + requestBody: + content: + application/ogcapppkg+json: + schema: + $ref: '#/components/schemas/ogcapppkg' + application/cwl: + schema: + $ref: '#/components/schemas/cwl' + application/cwl+json: + schema: + $ref: '#/components/schemas/cwl' + application/cwl+yaml: + schema: + $ref: '#/components/schemas/cwl' + description: An OGC Application Package used to deploy a new process. + required: true + responses: + "204": + description: successful operation (no response body) + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: the processes is not mutable + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: "The requested resource does not exist on the server. For example,\ + \ a path parameter had an incorrect value." + "409": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: the processes being added is already deployed (i.e. duplicate) + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: A server error occurred. + summary: replace a process. + tags: + - DRU + /processes/{processId}/execution: + post: + callbacks: + jobCompleted: + '{$request.body#/subscriber/successUri}': + post: + operationId: jobCompleted_Post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/results_1' + responses: + "200": + description: Results received successfully + x-callback-request: true + description: | + Executes a process (this may result in the creation of a job resource e.g., for _asynchronous execution_). + + For more information, see [Section 7.9](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_create_job). + operationId: execute + parameters: + - in: path + name: processId + required: true + schema: + $ref: '#/components/schemas/processes-list' + - description: |- + For executing the process using the _Collection Output_ mechanism, where the client is redirected (_303_ status) to either + an OGC API landing page or collection resource, from which one or more OGC API data access mechanism is available. + Data access requests may trigger processing on-demand for a given area, time and resolution of interest. + in: query + name: response + required: false + schema: + enum: + - collection + - landingPage + type: string + - description: |- + Indicates client preferences, including whether the client is capable of asynchronous processing. + A `respond-async` preference indicates a preference for asynchronous processing. + A `wait: s` preference indicates that the client prefers to wait up to x seconds to receive a reponse synchronously before the server falls back to asynchronous processing. + in: header + name: Prefer + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/execute-workflows' + description: "An execution request specifying any inputs for the process to\ + \ execute, and optionally to select specific outputs." + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/execute_200_response' + image/png: + schema: + format: binary + type: string + image/jpeg: + schema: + format: binary + type: string + image/tiff; application=geotiff: + schema: + format: binary + type: string + application/geo+json: + schema: + $ref: '#/components/schemas/execute_200_response_1' + description: Result of synchronous execution + "201": + content: + application/json: + schema: + $ref: '#/components/schemas/statusInfo' + description: Started asynchronous execution. Created job. + headers: + Location: + description: URL to check the status of the execution/job. + schema: + type: string + Preference-Applied: + description: The preference applied to execute the process asynchronously + (see. RFC 2740). + schema: + type: string + "303": + description: "For _Collection Output_ execution, redirection to an OGC API\ + \ landing page or collection." + headers: + Location: + description: |- + Location for redirection to an [OGC API landing page](https://schemas.opengis.net/ogcapi/tiles/part1/1.0/openapi/schemas/common-core/landingPage.yaml) (for `response=landingPage`) as described in + [OGC API - Common - Part 1: Core](http://docs.ogc.org/DRAFTS/19-072.html#_landing_page_requirements_class) or + an [OGC API collection description](https://schemas.opengis.net/ogcapi/tiles/part1/1.0/openapi/schemas/common-geodata/collectionInfo.yaml) (for `response=landingPage`) as described in + [OGC API - Common - Part 2: Geospatial data](http://docs.ogc.org/DRAFTS/20-024.html#collection-description). + The collection redirected to or the collections linked from the landing page redirected to + must contain links to at least one data access mechanism (such as [_OGC API - Tiles_](https://opengeospatial.github.io/ogcna-auto-review/20-057.html), + [_DGGS_](https://opengeospatial.github.io/ogcna-auto-review/21-038.html), + [_Coverages_](http://docs.ogc.org/DRAFTS/19-087.html), + [_Features_](https://docs.opengeospatial.org/is/17-069r4/17-069r4.html), + [_EDR_](https://docs.ogc.org/is/19-086r5/19-086r5.html), or + [_Maps_](http://docs.ogc.org/DRAFTS/20-058.html)...) to retrieve output results, which may + trigger on-demand processing. + schema: + type: string + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: "The requested resource does not exist on the server. For example,\ + \ a path parameter had an incorrect value." + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: A server error occurred. + summary: execute a process. + tags: + - Processes + /jobs: + get: + description: | + Lists available jobs. + + For more information, see [Section 12](https://docs.ogc.org/is/18-062r2/18-062r2.html#Job_list). + operationId: getJobs + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/jobList' + description: A list of jobs for this process. + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: "The requested resource does not exist on the server. For example,\ + \ a path parameter had an incorrect value." + summary: retrieve the list of jobs. + tags: + - Jobs + /jobs/{jobId}: + delete: + description: | + Cancel a job execution and remove it from the jobs list. + + For more information, see [Section 14]https://docs.ogc.org/is/18-062r2/18-062r2.html#Dismiss). + operationId: dismiss + parameters: + - description: local identifier of a job + in: path + name: jobId + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/statusInfo' + description: The status of a job. + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: "The requested resource does not exist on the server. For example,\ + \ a path parameter had an incorrect value." + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: A server error occurred. + summary: "cancel a job execution, remove a finished job" + tags: + - Jobs + get: + description: | + Shows the status of a job. + + For more information, see [Section 7.10](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_retrieve_status_info). + operationId: getStatus + parameters: + - description: local identifier of a job + in: path + name: jobId + required: true + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/statusInfo' + description: The status of a job. + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: "The requested resource does not exist on the server. For example,\ + \ a path parameter had an incorrect value." + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: A server error occurred. + summary: retrieve the status of a job + tags: + - Jobs + /jobs/{jobId}/results: + get: + description: | + Lists available results of a job. In case of a failure, lists exceptions instead. + + For more information, see [Section 7.11](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_retrieve_job_results). + operationId: getResult + parameters: + - description: local identifier of a job + in: path + name: jobId + required: true + schema: + type: string + - description: |- + Indicates client preferences, such as whether the client wishes a self-contained or minimal response. + A `return=minimal` preference indicates that the client would prefer that links be returned to larger object to minimize the response payload. + A `return=representation` indicates that the client would prefer if the server can return a self-contained response. + in: header + name: Prefer + schema: + type: string + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/results' + description: The processing results of a job. + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: "The requested resource does not exist on the server. For example,\ + \ a path parameter had an incorrect value." + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: A server error occurred. + summary: retrieve the result(s) of a job + tags: + - Jobs +components: + parameters: + f-metadata: + description: "The format of the response. If no value is provided, the accept\ + \ header is used to determine the format. Accepted values are 'json' or 'html'." + explode: false + in: query + name: f + required: false + schema: + enum: + - json + - html + type: string + style: form + datetime: + description: |- + Either a date-time or an interval, half-bounded or bounded. Date and time expressions + adhere to RFC 3339. Half-bounded intervals are expressed using double-dots. + + Examples: + + * A date-time: "2018-02-12T23:20:50Z" + * A bounded interval: "2018-02-12T00:00:00Z/2018-03-18T12:31:12Z" + * Half-bounded intervals: "2018-02-12T00:00:00Z/.." or "../2018-03-18T12:31:12Z" + + Only features that have a temporal property that intersects the value of + `datetime` are selected. + + If a feature has multiple temporal properties, it is the decision of the + server whether only a single temporal property is used to determine + the extent or all relevant temporal properties. + explode: false + in: query + name: datetime + required: false + schema: + type: string + style: form + crs: + description: reproject the output to the given crs + explode: true + in: query + name: crs + required: false + schema: + type: string + style: form + subset-crs: + description: crs for the specified subset + explode: true + in: query + name: subset-crs + required: false + schema: + type: string + style: form + processId-path: + in: path + name: processId + required: true + schema: + $ref: '#/components/schemas/processes-list' + outputId: + description: identifier of an output + in: path + name: outputID + required: true + schema: + type: string + jobId: + description: local identifier of a job + in: path + name: jobId + required: true + schema: + type: string + processId-query: + in: query + name: processID + required: false + schema: + items: + type: string + type: array + output: + explode: false + in: query + name: output + required: false + schema: + properties: + output: + type: string + mediaType: + type: string + encoding: + type: string + schema: + oneOf: + - format: url + type: string + - type: object + required: + - output + type: object + limit: + in: query + name: limit + required: false + schema: + default: 10 + maximum: 1000 + minimum: 1 + type: integer + type: + in: query + name: type + required: false + schema: + items: + enum: + - process + type: string + type: array + status: + in: query + name: status + required: false + schema: + items: + $ref: ./schemas/processes-core/statusCode.yaml + type: array + minDuration: + in: query + name: minDuration + required: false + schema: + type: integer + maxDuration: + in: query + name: maxDuration + required: false + schema: + type: integer + w: + $ref: '#/components/parameters/w-param' + w-param: + description: Point to the workflow identifier for deploying a CWL containing + multiple workflow definitions + in: query + name: w + required: false + schema: + type: string + response: + description: |- + For executing the process using the _Collection Output_ mechanism, where the client is redirected (_303_ status) to either + an OGC API landing page or collection resource, from which one or more OGC API data access mechanism is available. + Data access requests may trigger processing on-demand for a given area, time and resolution of interest. + in: query + name: response + required: false + schema: + enum: + - collection + - landingPage + type: string + prefer-header-execution: + description: |- + Indicates client preferences, including whether the client is capable of asynchronous processing. + A `respond-async` preference indicates a preference for asynchronous processing. + A `wait: s` preference indicates that the client prefers to wait up to x seconds to receive a reponse synchronously before the server falls back to asynchronous processing. + in: header + name: Prefer + schema: + type: string + prefer-header-results: + description: |- + Indicates client preferences, such as whether the client wishes a self-contained or minimal response. + A `return=minimal` preference indicates that the client would prefer that links be returned to larger object to minimize the response payload. + A `return=representation` indicates that the client would prefer if the server can return a self-contained response. + in: header + name: Prefer + schema: + type: string + responses: + NotFound: + $ref: '#/components/responses/rNotFound' + NotAcceptable: + $ref: '#/components/responses/rNotAcceptable' + ServerError: + $ref: '#/components/responses/rServerError' + InvalidParameter: + $ref: '#/components/responses/rInvalidParameter' + NotAllowed: + $ref: '#/components/responses/rNotAllowed' + Exception: + $ref: '#/components/responses/rException' + LandingPage: + $ref: '#/components/responses/rLandingPage' + Conformance: + $ref: '#/components/responses/rConformance' + API: + $ref: '#/components/responses/rAPI' + Enumeration: + $ref: '#/components/responses/rEnumeration' + ProcessList: + $ref: '#/components/responses/rProcessList' + ProcessDescription: + $ref: '#/components/responses/rProcessDescription' + Results: + $ref: '#/components/responses/rResults' + Status: + $ref: '#/components/responses/rStatus' + JobList: + $ref: '#/components/responses/rJobList' + ExecuteAsync: + $ref: '#/components/responses/rExecuteAsync' + ExecuteSync: + $ref: '#/components/responses/rExecuteSync' + ExecuteSyncRawRef: + $ref: '#/components/responses/rExecuteSyncRawRef' + EmptyResponse: + $ref: '#/components/responses/rEmpty' + DeployProcess: + $ref: '#/components/responses/rDeployProcess' + DuplicateProcess: + $ref: '#/components/responses/rDuplicateProcess' + ImmutableProcess: + $ref: '#/components/responses/rImmutableProcess' + rLandingPage: + content: + application/json: + schema: + $ref: '#/components/schemas/landingPage' + text/html: + schema: + type: string + description: |- + The landing page provides links to the API definition (link relation `service-desc`, in this case path `/api`), + to the Conformance declaration (path `/conformance`, link relation `http://www.opengis.net/def/rel/ogc/1.0/conformance`), and to other resources. + rNotAcceptable: + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: "Content negotiation failed. For example, the `Accept` header submitted\ + \ in the request did not support any of the media types supported by the server\ + \ for the requested resource." + rServerError: + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: A server error occurred. + rConformance: + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/confClasses' + example: + conformsTo: + - http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/core + - http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/json + - http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/html + - http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/oas30 + - http://www.opengis.net/spec/ogcapi-common-2/1.0/conf/collections + - http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/core + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/nested-processes + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/remote-core-processes + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/collection-input + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/remote-collections + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/input-fields-modifiers + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/output-fields-modifiers + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/deployable-workflows + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/collection-output + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/cwl-workflows + - http://www.opengis.net/spec/ogcapi-processes-3/0.0/conf/openeo-workflows + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/core + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tileset + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tilesets-list + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geodata-tilesets + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/dataset-tilesets + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geodata-selection + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/jpeg + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/png + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/mvt + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geojson + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tiff + - http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/netcdf + description: |- + The URIs of all conformance classes supported by the server + + To support "generic" clients that want to access multiple + OGC API - Processes implementations - and not "just" a specific + API / server, the server declares the conformance + classes it implements and conforms to. + rAPI: + content: + application/vnd.oai.openapi+json;version=3.0: + schema: + type: object + text/html: + schema: + type: string + description: The OpenAPI definition of the API. + rEnumeration: + content: + application/json: + schema: + $ref: '#/components/schemas/enumeration' + text/html: + schema: + type: string + description: An enumerated list of valid string values for API parameters. + rNotFound: + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: "The requested resource does not exist on the server. For example,\ + \ a path parameter had an incorrect value." + rProcessList: + content: + application/json: + schema: + $ref: '#/components/schemas/processList' + description: Information about the available processes + processSummary: {} + rImmutableProcess: + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: the processes is not mutable + rDuplicateProcess: + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: the processes being added is already deployed (i.e. duplicate) + rProcessDescription: + content: + application/json: + schema: + $ref: '#/components/schemas/process' + description: A process description. + rEmpty: + description: successful operation (no response body) + rExecuteSync: + content: + application/json: + schema: + $ref: '#/components/schemas/execute_200_response' + image/png: + schema: + format: binary + type: string + image/jpeg: + schema: + format: binary + type: string + image/tiff; application=geotiff: + schema: + format: binary + type: string + application/geo+json: + schema: + $ref: '#/components/schemas/execute_200_response_1' + description: Result of synchronous execution + rExecuteAsync: + content: + application/json: + schema: + $ref: '#/components/schemas/statusInfo' + description: Started asynchronous execution. Created job. + headers: + Location: + description: URL to check the status of the execution/job. + schema: + type: string + Preference-Applied: + description: The preference applied to execute the process asynchronously + (see. RFC 2740). + schema: + type: string + rExecuteCollectionRedirect: + description: "For _Collection Output_ execution, redirection to an OGC API landing\ + \ page or collection." + headers: + Location: + description: |- + Location for redirection to an [OGC API landing page](https://schemas.opengis.net/ogcapi/tiles/part1/1.0/openapi/schemas/common-core/landingPage.yaml) (for `response=landingPage`) as described in + [OGC API - Common - Part 1: Core](http://docs.ogc.org/DRAFTS/19-072.html#_landing_page_requirements_class) or + an [OGC API collection description](https://schemas.opengis.net/ogcapi/tiles/part1/1.0/openapi/schemas/common-geodata/collectionInfo.yaml) (for `response=landingPage`) as described in + [OGC API - Common - Part 2: Geospatial data](http://docs.ogc.org/DRAFTS/20-024.html#collection-description). + The collection redirected to or the collections linked from the landing page redirected to + must contain links to at least one data access mechanism (such as [_OGC API - Tiles_](https://opengeospatial.github.io/ogcna-auto-review/20-057.html), + [_DGGS_](https://opengeospatial.github.io/ogcna-auto-review/21-038.html), + [_Coverages_](http://docs.ogc.org/DRAFTS/19-087.html), + [_Features_](https://docs.opengeospatial.org/is/17-069r4/17-069r4.html), + [_EDR_](https://docs.ogc.org/is/19-086r5/19-086r5.html), or + [_Maps_](http://docs.ogc.org/DRAFTS/20-058.html)...) to retrieve output results, which may + trigger on-demand processing. + schema: + type: string + rJobList: + content: + application/json: + schema: + $ref: '#/components/schemas/jobList' + description: A list of jobs for this process. + rStatus: + content: + application/json: + schema: + $ref: '#/components/schemas/statusInfo' + description: The status of a job. + rResults: + content: + application/json: + schema: + $ref: '#/components/schemas/results' + description: The processing results of a job. + rInvalidParameter: + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: A query parameter has an invalid value. + rNotAllowed: + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: The method is not allowed at the path. + rException: + content: + application/json: + schema: + $ref: '#/components/schemas/exception' + text/html: + schema: + type: string + description: An error occured. + rExecuteSyncRawRef: + description: Synchronous execution response. + headers: + Link: + description: One or more Link headers pointing to each raw output. + schema: + type: string + rDeployProcess: + content: + application/json: + schema: + $ref: '#/components/schemas/staticIndicator' + description: the process is deployed + headers: + Location: + description: URL to fetch the processDescription of the deployed process + schema: + type: string + schemas: + confClasses: + properties: + conformsTo: + items: + example: http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/core + type: string + title: conformsTo + type: array + required: + - conformsTo + title: confClasses + type: object + link: + example: + hreflang: en + rel: service + href: href + type: application/json + title: title + properties: + href: + title: href + type: string + rel: + example: service + title: rel + type: string + type: + example: application/json + title: type + type: string + hreflang: + example: en + title: hreflang + type: string + title: + title: title + type: string + required: + - href + title: link + type: object + landingPage: + example: + attribution: attribution + description: Example server implementing the OGC API - Processes 1.0 Standard + links: + - hreflang: en + rel: service + href: href + type: application/json + title: title + - hreflang: en + rel: service + href: href + type: application/json + title: title + title: Example processing server + properties: + title: + example: Example processing server + title: title + type: string + description: + example: Example server implementing the OGC API - Processes 1.0 Standard + title: description + type: string + attribution: + description: "The `attribution` should be short and intended for presentation\ + \ to a user, for example, in a corner of a map. Parts of the text can\ + \ be links to other resources if additional information is needed. The\ + \ string can include HTML markup." + title: attribution for the Processes API + type: string + links: + items: + $ref: '#/components/schemas/link' + title: links + type: array + required: + - links + title: landingPage + type: object + exception: + additionalProperties: true + description: JSON schema for exceptions based on RFC 7807 + example: + instance: instance + detail: detail + type: type + title: title + status: 0 + properties: + type: + type: string + title: + type: string + status: + type: integer + detail: + type: string + instance: + type: string + required: + - type + title: Exception Schema + type: object + collections: + properties: + links: + items: + $ref: '#/components/schemas/link' + type: array + timeStamp: + format: date-time + type: string + numberMatched: + example: 1 + minimum: 0 + type: integer + numberReturned: + example: 1 + minimum: 0 + type: integer + collections: + items: + $ref: '#/components/schemas/collectionInfo' + type: array + required: + - collections + - links + type: object + collectionInfo: + properties: + id: + description: "identifier of the collection used, for example, in URIs" + example: dem + title: id + type: string + title: + description: human readable title of the collection + example: Digital Elevation Model + title: title + type: string + description: + description: a description of the data in the collection + example: A Digital Elevation Model. + title: description + type: string + links: + example: + - href: http://data.example.org/collections/dem?f=json + rel: self + type: application/json + title: Digital Elevation Model + - href: http://data.example.org/collections/dem?f=html + rel: alternate + type: application/json + title: Digital Elevation Model + - href: http://data.example.org/collections/dem/coverage + rel: coverage + type: image/tiff; application=geotiff + title: Digital Elevation Model + - href: http://data.example.org/collections/dem/coverage/domainset + rel: domainset + type: application/json + title: Digital Elevation Model + - href: http://data.example.org/collections/dem/coverage/rangetype + rel: rangetype + type: application/json + title: Digital Elevation Model + - href: http://data.example.org/collections/dem/coverage/metadata + rel: metadata + type: application/json + title: Digital Elevation Model + items: + $ref: '#/components/schemas/link' + title: links + type: array + extent: + $ref: '#/components/schemas/extent-uad' + itemType: + default: unknown + description: "indicator about the type of the items in the collection if\ + \ the collection has an accessible /collections/{collectionId}/items endpoint" + title: itemType + type: string + crs: + default: + - http://www.opengis.net/def/crs/OGC/1.3/CRS84 + description: the list of coordinate reference systems supported by the API; + the first item is the default coordinate reference system + example: + - http://www.opengis.net/def/crs/OGC/1.3/CRS84 + - http://www.opengis.net/def/crs/EPSG/0/4326 + items: + type: string + title: crs + type: array + dataType: + $ref: '#/components/schemas/collectionInfo_dataType' + geometryDimension: + description: "The geometry dimension of the features shown in this layer\ + \ (0: points, 1: curves, 2: surfaces, 3: solids), unspecified: mixed or\ + \ unknown" + maximum: 3 + minimum: 0 + title: geometryDimension + type: integer + minScaleDenominator: + description: Minimum scale denominator for usage of the collection + title: minScaleDenominator + type: number + maxScaleDenominator: + description: Maximum scale denominator for usage of the collection + title: maxScaleDenominator + type: number + minCellSize: + description: Minimum cell size for usage of the collection + title: minCellSize + type: number + maxCellSize: + description: Maximum cell size for usage of the collection + title: maxCellSize + type: number + required: + - id + - links + title: collectionInfo + type: object + extent: + description: |- + The extent of the data in the collection. In the Core only spatial and temporal + extents are specified. Extensions may add additional members to represent other + extents, for example, thermal or pressure ranges. + + The first item in the array describes the overall extent of + the data. All subsequent items describe more precise extents, + e.g., to identify clusters of data. + Clients only interested in the overall extent will only need to + access the first item in each array. + properties: + spatial: + $ref: '#/components/schemas/extent_spatial' + temporal: + $ref: '#/components/schemas/extent_temporal' + title: extent + type: object + extent-uad: + allOf: + - $ref: '#/components/schemas/extent' + - additionalProperties: + description: The domain intervals for any additional dimensions of the extent + (envelope) beyond those described in temporal and spatial. + oneOf: + - required: + - crs + - interval + type: object + - required: + - interval + - trs + type: object + - required: + - interval + - vrs + type: object + properties: + interval: + description: |- + One or more intervals that describe the extent for this dimension of the dataset. + The value `null` is supported and indicates an unbounded or half-bounded interval. + The first interval describes the overall extent of the data for this dimension. + All subsequent intervals describe more precise intervals, e.g., to identify clusters of data. + Clients only interested in the overall extent will only need + to access the first item (a pair of lower and upper bound values). + items: + description: |- + Lower and upper bound values of the interval. The values + are in the coordinate reference system specified in `crs`, `trs` or `vrs`. + example: + - 2011-11-11T12:22:11Z + - 32.5 + - null + items: + oneOf: + - nullable: true + type: string + - type: number + maxItems: 2 + minItems: 2 + type: array + minItems: 1 + type: array + crs: + description: generic coordinate reference system suitable for any type + of dimensions + type: string + trs: + description: temporal coordinate reference system (e.g. as defined by + Features for 'temporal') + type: string + vrs: + description: vertical coordinate reference system (e.g. as defined in + EDR for 'vertical') + type: string + grid: + description: Provides information about the limited availability of + data within the collection organized as a grid (regular or irregular) + along the dimension. + properties: + coordinates: + description: |- + List of coordinates along the temporal dimension for which data organized as an irregular grid in the collection is available + (e.g., 2, 10, 80, 100). + example: + - 2 + - 10 + - 80 + - 100 + items: + oneOf: + - nullable: true + type: string + - type: number + minItems: 1 + type: array + cellsCount: + description: |- + Number of samples available along the dimension for data organized as a regular grid. + For values representing the whole area of contiguous cells spanning _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_. + For values representing infinitely small point cells spaced by _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_ + 1. + example: 50 + type: integer + resolution: + description: Resolution of regularly gridded data along the dimension + in the collection + example: + - PT1H + - 6.866455078E-4 + oneOf: + - nullable: true + type: string + - type: number + type: object + type: object + type: object + description: |- + The extent module only addresses spatial and temporal extents. This module extends extent by specifying how + intervals and crs properties can be used to specify additional geometries. + title: Extent with Uniform Additional Dimensions Schema + crs: + oneOf: + - description: Simplification of the object into a url if the other properties + are not present + type: string + - $ref: '#/components/schemas/crs_oneOf' + title: CRS + dataType: + oneOf: + - type: string + - enum: + - map + - vector + - coverage + type: string + title: dataType + timeStamp: + description: This property indicates the time and date when the response was + generated using RFC 3339 notation. + example: 2017-08-17T08:05:32Z + format: date-time + type: string + numberReturned: + description: |- + The number of features in the feature collection. + A server may omit this information in a response, if the information + about the number of features is not known or difficult to compute. + If the value is provided, the value shall be identical to the number + of items in the "features" array. + example: 10 + minimum: 0 + type: integer + numberMatched: + description: |- + The number of features of the feature type that match the selection + parameters like `bbox`. + example: 127 + minimum: 0 + type: integer + enumeration: + example: + type: enum + enum: + - enum + - enum + properties: + type: + enum: + - enum + title: type + type: string + enum: + items: + type: string + title: enum + type: array + required: + - enum + - type + title: enumeration + type: object + processes-list: + enum: + - RenderMap + - ElevationContours + - OSMERE + title: processes-list + type: string + processSummary: + allOf: + - $ref: '#/components/schemas/descriptionType' + - properties: + id: + type: string + version: + type: string + jobControlOptions: + items: + $ref: '#/components/schemas/jobControlOptions' + type: array + links: + items: + $ref: '#/components/schemas/link' + type: array + required: + - id + - version + type: object + example: + metadata: + - hreflang: en + role: role + rel: service + href: href + type: application/json + title: title + - hreflang: en + role: role + rel: service + href: href + type: application/json + title: title + keywords: + - keywords + - keywords + description: description + links: + - hreflang: en + rel: service + href: href + type: application/json + title: title + - hreflang: en + rel: service + href: href + type: application/json + title: title + id: id + title: title + version: version + jobControlOptions: + - sync-execute + - sync-execute + title: processSummary + process: + allOf: + - $ref: '#/components/schemas/processSummary' + - properties: + inputs: + additionalProperties: + $ref: '#/components/schemas/inputDescription' + type: object + outputs: + additionalProperties: + $ref: '#/components/schemas/outputDescription' + type: object + type: object + example: + outputs: + key: + schema: null + metadata: + - null + - null + keywords: + - keywords + - keywords + description: description + title: title + metadata: + - hreflang: en + role: role + rel: service + href: href + type: application/json + title: title + - hreflang: en + role: role + rel: service + href: href + type: application/json + title: title + keywords: + - keywords + - keywords + inputs: + key: + schema: + $ref: $ref + metadata: + - null + - null + keywords: + - keywords + - keywords + minOccurs: 0 + valuePassing: + - byValue + - byValue + description: description + maxOccurs: 6 + title: title + description: description + links: + - hreflang: en + rel: service + href: href + type: application/json + title: title + - hreflang: en + rel: service + href: href + type: application/json + title: title + id: id + title: title + version: version + jobControlOptions: + - sync-execute + - sync-execute + title: process + processList: + example: + processes: + - metadata: + - hreflang: en + role: role + rel: service + href: href + type: application/json + title: title + - hreflang: en + role: role + rel: service + href: href + type: application/json + title: title + keywords: + - keywords + - keywords + description: description + links: + - hreflang: en + rel: service + href: href + type: application/json + title: title + - hreflang: en + rel: service + href: href + type: application/json + title: title + id: id + title: title + version: version + jobControlOptions: + - sync-execute + - sync-execute + - metadata: + - hreflang: en + role: role + rel: service + href: href + type: application/json + title: title + - hreflang: en + role: role + rel: service + href: href + type: application/json + title: title + keywords: + - keywords + - keywords + description: description + links: + - hreflang: en + rel: service + href: href + type: application/json + title: title + - hreflang: en + rel: service + href: href + type: application/json + title: title + id: id + title: title + version: version + jobControlOptions: + - sync-execute + - sync-execute + links: + - hreflang: en + rel: service + href: href + type: application/json + title: title + - hreflang: en + rel: service + href: href + type: application/json + title: title + properties: + processes: + items: + $ref: '#/components/schemas/processSummary' + title: processes + type: array + links: + items: + $ref: '#/components/schemas/link' + title: links + type: array + required: + - links + - processes + title: processList + type: object + jobList: + example: + jobs: + - exception: + instance: instance + detail: detail + type: type + title: title + status: 0 + jobID: jobID + processID: processID + created: 2000-01-23T04:56:07.000+00:00 + progress: 8 + started: 2000-01-23T04:56:07.000+00:00 + finished: 2000-01-23T04:56:07.000+00:00 + links: + - hreflang: en + rel: service + href: href + type: application/json + title: title + - hreflang: en + rel: service + href: href + type: application/json + title: title + type: process + message: message + updated: 2000-01-23T04:56:07.000+00:00 + status: accepted + - exception: + instance: instance + detail: detail + type: type + title: title + status: 0 + jobID: jobID + processID: processID + created: 2000-01-23T04:56:07.000+00:00 + progress: 8 + started: 2000-01-23T04:56:07.000+00:00 + finished: 2000-01-23T04:56:07.000+00:00 + links: + - hreflang: en + rel: service + href: href + type: application/json + title: title + - hreflang: en + rel: service + href: href + type: application/json + title: title + type: process + message: message + updated: 2000-01-23T04:56:07.000+00:00 + status: accepted + links: + - hreflang: en + rel: service + href: href + type: application/json + title: title + - hreflang: en + rel: service + href: href + type: application/json + title: title + properties: + jobs: + items: + $ref: '#/components/schemas/statusInfo' + title: jobs + type: array + links: + items: + $ref: '#/components/schemas/link' + title: links + type: array + required: + - jobs + - links + title: jobList + type: object + bbox-processes: + properties: + bbox: + items: + type: number + oneOf: + - maxItems: 4 + minItems: 4 + type: object + - maxItems: 6 + minItems: 6 + type: object + title: bbox + type: array + crs: + $ref: '#/components/schemas/bbox-def-crs' + required: + - bbox + title: bbox + type: object + descriptionType: + properties: + title: + title: title + type: string + description: + title: description + type: string + keywords: + items: + type: string + title: keywords + type: array + metadata: + items: + $ref: '#/components/schemas/metadata' + title: metadata + type: array + title: descriptionType + type: object + binaryInputValue: + format: byte + type: string + execute: + properties: + inputs: + additionalProperties: + $ref: '#/components/schemas/input' + type: object + outputs: + additionalProperties: + $ref: '#/components/schemas/output' + type: object + subscriber: + $ref: '#/components/schemas/subscriber' + type: object + format: + properties: + mediaType: + title: mediaType + type: string + encoding: + title: encoding + type: string + schema: + $ref: '#/components/schemas/format_schema' + title: format + type: object + inputDescription: + allOf: + - $ref: '#/components/schemas/descriptionType' + - properties: + schema: + $ref: '#/components/schemas/schema' + minOccurs: + default: 1 + type: integer + maxOccurs: + $ref: '#/components/schemas/inputDescription_allOf_maxOccurs' + valuePassing: + default: + - byValue + - byReference + items: + enum: + - byValue + - byReference + type: string + type: array + required: + - schema + type: object + example: + schema: + $ref: $ref + metadata: + - null + - null + keywords: + - keywords + - keywords + minOccurs: 0 + valuePassing: + - byValue + - byValue + description: description + maxOccurs: 6 + title: title + title: inputDescription + inputValue: + anyOf: + - $ref: '#/components/schemas/inputValueNoObject' + - type: object + title: inputValue + inputValueNoObject: + oneOf: + - type: string + - type: number + - type: integer + - type: boolean + - items: + type: object + type: array + - $ref: '#/components/schemas/binaryInputValue' + - $ref: '#/components/schemas/bbox' + title: inputValueNoObject + jobControlOptions: + enum: + - sync-execute + - async-execute + - dismiss + title: jobControlOptions + type: string + metadata: + oneOf: + - $ref: '#/components/schemas/metadata_oneOf' + - $ref: '#/components/schemas/metadata_oneOf_1' + title: metadata + output: + properties: + format: + $ref: '#/components/schemas/format' + title: output + type: object + outputDescription: + allOf: + - $ref: '#/components/schemas/descriptionType' + - properties: + schema: + $ref: '#/components/schemas/schema' + required: + - schema + type: object + example: + schema: null + metadata: + - null + - null + keywords: + - keywords + - keywords + description: description + title: title + title: outputDescription + qualifiedInputValue: + allOf: + - $ref: '#/components/schemas/format' + - properties: + value: + $ref: '#/components/schemas/inputValue' + required: + - value + type: object + title: qualifiedInputValue + reference: + additionalProperties: false + example: + $ref: $ref + properties: + $ref: + format: uri-reference + title: $ref + type: string + required: + - $ref + title: reference + type: object + results: + additionalProperties: + $ref: '#/components/schemas/inlineOrRefData' + type: object + schema: + oneOf: + - $ref: '#/components/schemas/reference' + - $ref: '#/components/schemas/schema_oneOf' + title: schema + statusCode: + enum: + - accepted + - running + - successful + - failed + - dismissed + nullable: false + title: statusCode + type: string + subscriber: + description: |- + Optional URIs for callbacks for this job. + + Support for this parameter is not required and the parameter may be + removed from the API definition, if conformance class **'callback'** + is not listed in the conformance declaration under `/conformance`. + properties: + successUri: + format: uri + title: successUri + type: string + inProgressUri: + format: uri + title: inProgressUri + type: string + failedUri: + format: uri + title: failedUri + type: string + required: + - successUri + title: subscriber + type: object + inlineOrRefData: + oneOf: + - $ref: '#/components/schemas/inputValueNoObject' + - $ref: '#/components/schemas/qualifiedInputValue' + - $ref: '#/components/schemas/link' + title: inlineOrRefData + statusInfo: + example: + exception: + instance: instance + detail: detail + type: type + title: title + status: 0 + jobID: jobID + processID: processID + created: 2000-01-23T04:56:07.000+00:00 + progress: 8 + started: 2000-01-23T04:56:07.000+00:00 + finished: 2000-01-23T04:56:07.000+00:00 + links: + - hreflang: en + rel: service + href: href + type: application/json + title: title + - hreflang: en + rel: service + href: href + type: application/json + title: title + type: process + message: message + updated: 2000-01-23T04:56:07.000+00:00 + status: accepted + properties: + processID: + title: processID + type: string + type: + enum: + - process + title: type + type: string + jobID: + title: jobID + type: string + status: + $ref: '#/components/schemas/statusCode' + message: + title: message + type: string + exception: + $ref: '#/components/schemas/exception' + created: + format: date-time + title: created + type: string + started: + format: date-time + title: started + type: string + finished: + format: date-time + title: finished + type: string + updated: + format: date-time + title: updated + type: string + progress: + maximum: 100 + minimum: 0 + title: progress + type: integer + links: + items: + $ref: '#/components/schemas/link' + title: links + type: array + required: + - jobID + - status + - type + title: statusInfo + type: object + ogcapppkg: + properties: + processDescription: + $ref: '#/components/schemas/process' + executionUnit: + $ref: '#/components/schemas/ogcapppkg_executionUnit' + required: + - executionUnit + title: ogcapppkg + type: object + staticIndicator: + allOf: + - $ref: '#/components/schemas/processSummary' + - properties: + mutable: + default: true + type: boolean + type: object + cwl: + type: object + execute-workflows: + allOf: + - properties: + process: + description: "URI to the process execution end point (i.e., `.../processes/{processId}/execution`)" + format: uri-reference + type: string + inputs: + additionalProperties: + $ref: '#/components/schemas/input-workflows' + type: object + outputs: + additionalProperties: + $ref: '#/components/schemas/output-workflows' + type: object + subscriber: + $ref: '#/components/schemas/subscriber' + type: object + - $ref: '#/components/schemas/fieldsModifiers' + title: execute-workflows + FeatureCollection: + properties: + type: + enum: + - FeatureCollection + title: type + type: string + features: + items: + $ref: '#/components/schemas/GeoJSON_Feature' + title: features + type: array + bbox: + items: + type: number + minItems: 4 + title: bbox + type: array + required: + - features + - type + title: GeoJSON FeatureCollection + type: object + results_1: + additionalProperties: + $ref: '#/components/schemas/inlineOrRefData' + type: object + bbox: + properties: + bbox: + items: + type: number + oneOf: + - maxItems: 4 + minItems: 4 + type: object + - maxItems: 6 + minItems: 6 + type: object + title: bbox + type: array + crs: + $ref: '#/components/schemas/bbox-def-crs' + required: + - bbox + title: bbox + type: object + schema_1: + oneOf: + - $ref: '#/components/schemas/reference' + - $ref: '#/components/schemas/schema_oneOf' + title: schema_1 + executionUnit: + additionalProperties: true + description: Resource containing an executable or runtime information for executing + the process. + example: + type: docker + image: mydocker/ndvi:latest + deployment: local + config: + cpuMin: 2 + cpuMax: 5 + memoryMin: 1 + memoryMax: 3 + properties: + type: + description: Type of execution unit. + enum: + - docker + - oci + type: string + image: + description: Container image reference for the execution unit. + type: string + deployment: + description: Deployment information for the execution unit. + enum: + - local + - remote + - hpc + - cloud + type: string + config: + $ref: '#/components/schemas/executionUnit_config' + required: + - image + - type + title: executionUnit + type: object + ogcapppkg-array: + items: + $ref: '#/components/schemas/ogcapppkg_array_inner' + type: array + input-workflows: + oneOf: + - $ref: '#/components/schemas/inlineOrRefData-workflows' + - items: + $ref: '#/components/schemas/inlineOrRefData-workflows' + type: array + title: input-workflows + output-workflows: + properties: + format: + $ref: '#/components/schemas/format' + $output: + title: $output + type: string + title: output-workflows + type: object + fieldsModifiers: + properties: + filter: + title: filter + type: string + properties: + $ref: '#/components/schemas/fieldsModifiers_properties' + sortBy: + items: + type: string + title: sortBy + type: array + title: fieldsModifiers + type: object + bbox-def-crs: + anyOf: + - default: http://www.opengis.net/def/crs/OGC/1.3/CRS84 + enum: + - http://www.opengis.net/def/crs/OGC/1.3/CRS84 + - http://www.opengis.net/def/crs/OGC/0/CRS84h + format: uri + type: string + - default: http://www.opengis.net/def/crs/OGC/1.3/CRS84 + format: uri + type: string + title: bbox-def-crs + input: + oneOf: + - $ref: '#/components/schemas/inlineOrRefData_1' + - items: + $ref: '#/components/schemas/inlineOrRefData_1' + type: array + title: input + inlineOrRefData-workflows: + oneOf: + - $ref: '#/components/schemas/inputValueNoObject-workflows' + - $ref: '#/components/schemas/qualifiedInputValue-workflows' + - $ref: '#/components/schemas/link' + title: inlineOrRefData-workflows + inlineOrRefData_1: + oneOf: + - $ref: '#/components/schemas/inputValueNoObject_1' + - $ref: '#/components/schemas/qualifiedInputValue_1' + - $ref: '#/components/schemas/link' + title: inlineOrRefData_1 + inputValueNoObject-workflows: + oneOf: + - type: string + - type: number + - type: integer + - type: boolean + - items: + type: object + type: array + - $ref: '#/components/schemas/binaryInputValue' + - $ref: '#/components/schemas/bbox_1' + - $ref: '#/components/schemas/inputCollection' + - $ref: '#/components/schemas/inputProcess' + - $ref: '#/components/schemas/inputParameterized' + title: inputValueNoObject-workflows + qualifiedInputValue-workflows: + allOf: + - $ref: '#/components/schemas/format' + - $ref: '#/components/schemas/fieldsModifiers' + - properties: + value: + $ref: '#/components/schemas/inputValue-workflows' + required: + - value + type: object + title: qualifiedInputValue-workflows + inputValueNoObject_1: + oneOf: + - type: string + - type: number + - type: integer + - type: boolean + - items: + type: object + type: array + - $ref: '#/components/schemas/binaryInputValue' + - $ref: '#/components/schemas/bbox_1' + title: inputValueNoObject_1 + qualifiedInputValue_1: + allOf: + - $ref: '#/components/schemas/format' + - properties: + value: + $ref: '#/components/schemas/inputValue_1' + required: + - value + type: object + title: qualifiedInputValue_1 + bbox_1: + properties: + bbox: + items: + type: number + oneOf: + - maxItems: 4 + minItems: 4 + type: object + - maxItems: 6 + minItems: 6 + type: object + title: bbox + type: array + crs: + $ref: '#/components/schemas/bbox-def-crs' + required: + - bbox + title: bbox_1 + type: object + inputCollection: + allOf: + - properties: + collection: + format: uri-reference + type: string + required: + - collection + type: object + - $ref: '#/components/schemas/fieldsModifiers' + title: inputCollection + inputProcess: + allOf: + - required: + - process + type: object + - $ref: '#/components/schemas/execute-workflows_1' + title: inputProcess + inputParameterized: + allOf: + - properties: + $input: + type: string + required: + - $input + type: object + - $ref: '#/components/schemas/fieldsModifiers' + title: inputParameterized + inputValue-workflows: + oneOf: + - $ref: '#/components/schemas/inputValueNoObject-workflows' + - type: object + title: inputValue-workflows + inputValue_1: + anyOf: + - $ref: '#/components/schemas/inputValueNoObject_1' + - type: object + title: inputValue_1 + execute-workflows_1: + allOf: + - properties: + process: + description: "URI to the process execution end point (i.e., `.../processes/{processId}/execution`)" + format: uri-reference + type: string + inputs: + additionalProperties: + $ref: '#/components/schemas/input-workflows_1' + type: object + outputs: + additionalProperties: + $ref: '#/components/schemas/output-workflows_1' + type: object + subscriber: + $ref: '#/components/schemas/subscriber' + type: object + - $ref: '#/components/schemas/fieldsModifiers' + title: execute-workflows_1 + input-workflows_1: + oneOf: + - $ref: '#/components/schemas/inlineOrRefData-workflows' + - items: + $ref: '#/components/schemas/inlineOrRefData-workflows' + type: array + title: input-workflows_1 + output-workflows_1: + properties: + format: + $ref: '#/components/schemas/format' + $output: + title: $output + type: string + title: output-workflows_1 + type: object + execute_200_response: + oneOf: + - type: string + - type: number + - type: integer + - nullable: true + type: object + - items: + type: object + type: array + - type: boolean + - format: binary + type: string + - $ref: '#/components/schemas/results' + title: execute_200_response + execute_200_response_1: + allOf: + - format: geojson-feature-collection + type: object + - $ref: '#/components/schemas/FeatureCollection' + title: execute_200_response_1 + collectionInfo_dataType: + allOf: + - description: Type of data represented in the collection + type: object + - $ref: '#/components/schemas/dataType' + title: collectionInfo_dataType + extent_spatial_bbox_inner: + description: |- + Each bounding box is provided as four or six numbers, depending on + whether the coordinate reference system includes a vertical axis + (height or depth): + + * Lower left corner, coordinate axis 1 + * Lower left corner, coordinate axis 2 + * Minimum value, coordinate axis 3 (optional) + * Upper right corner, coordinate axis 1 + * Upper right corner, coordinate axis 2 + * Maximum value, coordinate axis 3 (optional) + + If the value consists of four numbers, the coordinate reference system is + WGS 84 longitude/latitude (http://www.opengis.net/def/crs/OGC/1.3/CRS84) + unless a different coordinate reference system is specified in a parameter `bbox-crs`. + + If the value consists of six numbers, the coordinate reference system is WGS 84 + longitude/latitude/ellipsoidal height (http://www.opengis.net/def/crs/OGC/0/CRS84h) + unless a different coordinate reference system is specified in a parameter `bbox-crs`. + + For WGS 84 longitude/latitude the values are in most cases the sequence of + minimum longitude, minimum latitude, maximum longitude and maximum latitude. + However, in cases where the box spans the antimeridian the first value + (west-most box edge) is larger than the third value (east-most box edge). + + If the vertical axis is included, the third and the sixth number are + the bottom and the top of the 3-dimensional bounding box. + + If a feature has multiple spatial geometry properties, it is the decision of the + server whether only a single spatial geometry property is used to determine + the extent or all relevant geometries. + example: + - -180 + - -90 + - 180 + - 90 + items: + type: number + oneOf: + - maxItems: 4 + minItems: 4 + type: object + - maxItems: 6 + minItems: 6 + type: object + title: extent_spatial_bbox_inner + type: array + extent_spatial_grid_inner_coordinates_inner: + oneOf: + - nullable: true + type: string + - type: number + title: extent_spatial_grid_inner_coordinates_inner + extent_spatial_grid_inner_resolution: + description: Resolution of regularly gridded data along the dimension in the + collection + example: 6.866455078E-4 + oneOf: + - nullable: true + type: string + - type: number + title: extent_spatial_grid_inner_resolution + extent_spatial_grid_inner: + properties: + coordinates: + description: |- + List of coordinates along the dimension for which data organized as an irregular grid in the collection is available + (e.g., 2, 10, 80, 100). + example: + - 2 + - 10 + - 80 + - 100 + items: + $ref: '#/components/schemas/extent_spatial_grid_inner_coordinates_inner' + minItems: 1 + title: coordinates + type: array + cellsCount: + description: |- + Number of samples available along the dimension for data organized as a regular grid. + For values representing the whole area of contiguous cells spanning _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_. + For values representing infinitely small point cells spaced by _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_ + 1. + example: 50 + title: cellsCount + type: integer + resolution: + $ref: '#/components/schemas/extent_spatial_grid_inner_resolution' + title: extent_spatial_grid_inner + type: object + extent_spatial: + description: The spatial extent of the data in the collection. + properties: + bbox: + description: |- + One or more bounding boxes that describe the spatial extent of the dataset. + In the Core only a single bounding box is supported. + + Extensions may support additional areas. + The first bounding box describes the overall spatial + extent of the data. All subsequent bounding boxes describe + more precise bounding boxes, e.g., to identify clusters of data. + Clients only interested in the overall spatial extent will + only need to access the first item in each array. + items: + $ref: '#/components/schemas/extent_spatial_bbox_inner' + minItems: 1 + title: bbox + type: array + crs: + default: http://www.opengis.net/def/crs/OGC/1.3/CRS84 + description: |- + Coordinate reference system of the coordinates in the spatial extent + (property `bbox`). The default reference system is WGS 84 longitude/latitude. + In the Core the only other supported coordinate reference system is + WGS 84 longitude/latitude/ellipsoidal height for coordinates with height. + Extensions may support additional coordinate reference systems and add + additional enum values. + enum: + - http://www.opengis.net/def/crs/OGC/1.3/CRS84 + - http://www.opengis.net/def/crs/OGC/0/CRS84h + title: crs + type: string + grid: + description: |- + Provides information about the limited availability of data within the collection organized + as a grid (regular or irregular) along each spatial dimension. + items: + $ref: '#/components/schemas/extent_spatial_grid_inner' + maxItems: 3 + minItems: 2 + title: grid + type: array + title: extent_spatial + type: object + extent_temporal_grid_resolution: + description: Resolution of regularly gridded data along the temporal dimension + in the collection + example: PT1H + oneOf: + - nullable: true + type: string + - type: number + title: extent_temporal_grid_resolution + extent_temporal_grid: + description: Provides information about the limited availability of data within + the collection organized as a grid (regular or irregular) along the temporal + dimension. + properties: + coordinates: + description: |- + List of coordinates along the temporal dimension for which data organized as an irregular grid in the collection is available + (e.g., "2017-11-14T09:00Z","2017-11-14T12:00Z","2017-11-14T15:00Z","2017-11-14T18:00Z","2017-11-14T21:00Z"). + example: + - - 2020-11-12T12:15Z + - 2020-11-12T12:30Z + - 2020-11-12T12:45Z + items: + nullable: true + type: string + minItems: 1 + title: coordinates + type: array + cellsCount: + description: |- + Number of samples available along the temporal dimension for data organized as a regular grid. + For values representing the whole area of contiguous cells spanning _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_. + For values representing infinitely small point cells spaced by _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_ + 1. + example: 50 + title: cellsCount + type: integer + resolution: + $ref: '#/components/schemas/extent_temporal_grid_resolution' + title: extent_temporal_grid + type: object + extent_temporal: + description: The temporal extent of the features in the collection. + properties: + interval: + description: |- + One or more time intervals that describe the temporal extent of the dataset. + In the Core only a single time interval is supported. + + Extensions may support multiple intervals. + The first time interval describes the overall + temporal extent of the data. All subsequent time intervals describe + more precise time intervals, e.g., to identify clusters of data. + Clients only interested in the overall extent will only need + to access the first item in each array. + items: + description: |- + Begin and end times of the time interval. The timestamps are in the + temporal coordinate reference system specified in `trs`. By default + this is the Gregorian calendar. + + The value `null` for start or end time is supported and indicates a half-bounded time interval. + example: + - 2011-11-11T12:22:11Z + - null + items: + format: date-time + nullable: true + type: string + maxItems: 2 + minItems: 2 + type: array + minItems: 1 + title: interval + type: array + trs: + default: http://www.opengis.net/def/uom/ISO-8601/0/Gregorian + description: |- + Coordinate reference system of the coordinates in the temporal extent + (property `interval`). The default reference system is the Gregorian calendar. + In the Core this is the only supported temporal coordinate reference system. + Extensions may support additional temporal coordinate reference systems and add + additional enum values. + enum: + - http://www.opengis.net/def/uom/ISO-8601/0/Gregorian + title: trs + type: string + grid: + $ref: '#/components/schemas/extent_temporal_grid' + title: extent_temporal + type: object + crs_oneOf_oneOf: + properties: + uri: + description: Reference to one coordinate reference system (CRS) + format: uri + title: uri + type: string + required: + - uri + title: crs_oneOf_oneOf + type: object + crs_oneOf_oneOf_1: + properties: + wkt: + allOf: + - description: An object defining the CRS using the JSON encoding for Well-known + text representation of coordinate reference systems 2.0 + type: object + - type: object + title: wkt + required: + - wkt + title: crs_oneOf_oneOf_1 + type: object + crs_oneOf_oneOf_2: + properties: + referenceSystem: + description: A reference system data structure as defined in the MD_ReferenceSystem + of the ISO 19115 + title: referenceSystem + type: object + required: + - referenceSystem + title: crs_oneOf_oneOf_2 + type: object + crs_oneOf: + oneOf: + - $ref: '#/components/schemas/crs_oneOf_oneOf' + - $ref: '#/components/schemas/crs_oneOf_oneOf_1' + - $ref: '#/components/schemas/crs_oneOf_oneOf_2' + title: crs_oneOf + type: object + bbox_processes_bbox: + items: + type: number + oneOf: + - maxItems: 4 + minItems: 4 + type: object + - maxItems: 6 + minItems: 6 + type: object + title: bbox + type: array + format_schema: + oneOf: + - format: url + type: string + - type: object + title: format_schema + inputDescription_allOf_maxOccurs: + oneOf: + - default: 1 + type: integer + - enum: + - unbounded + type: string + title: inputDescription_allOf_maxOccurs + metadata_oneOf: + allOf: + - $ref: '#/components/schemas/link' + - properties: + role: + type: string + type: object + example: + hreflang: en + role: role + rel: service + href: href + type: application/json + title: title + title: metadata_oneOf + metadata_oneOf_1_value: + oneOf: + - type: string + - type: object + title: metadata_oneOf_1_value + metadata_oneOf_1: + properties: + role: + title: role + type: string + title: + title: title + type: string + lang: + title: lang + type: string + value: + $ref: '#/components/schemas/metadata_oneOf_1_value' + title: metadata_oneOf_1 + type: object + schema_oneOf_additionalProperties: + default: true + oneOf: + - $ref: '#/components/schemas/schema_1' + - type: boolean + title: schema_oneOf_additionalProperties + schema_oneOf: + additionalProperties: false + properties: + title: + title: title + type: string + multipleOf: + exclusiveMinimum: true + minimum: 0 + title: multipleOf + type: number + maximum: + title: maximum + type: number + exclusiveMaximum: + default: false + title: exclusiveMaximum + type: boolean + minimum: + title: minimum + type: number + exclusiveMinimum: + default: false + title: exclusiveMinimum + type: boolean + maxLength: + minimum: 0 + title: maxLength + type: integer + minLength: + default: 0 + minimum: 0 + title: minLength + type: integer + pattern: + format: regex + title: pattern + type: string + maxItems: + minimum: 0 + title: maxItems + type: integer + minItems: + default: 0 + minimum: 0 + title: minItems + type: integer + uniqueItems: + default: false + title: uniqueItems + type: boolean + maxProperties: + minimum: 0 + title: maxProperties + type: integer + minProperties: + default: 0 + minimum: 0 + title: minProperties + type: integer + required: + items: + type: string + minItems: 1 + title: required + type: array + uniqueItems: true + enum: + items: + type: object + minItems: 1 + title: enum + type: array + uniqueItems: false + type: + enum: + - array + - boolean + - integer + - number + - object + - string + title: type + type: string + not: + $ref: '#/components/schemas/schema_1' + allOf: + items: + $ref: '#/components/schemas/schema_1' + title: allOf + type: array + oneOf: + items: + $ref: '#/components/schemas/schema_1' + title: oneOf + type: array + anyOf: + items: + $ref: '#/components/schemas/schema_1' + title: anyOf + type: array + items: + $ref: '#/components/schemas/schema_1' + properties: + additionalProperties: + $ref: '#/components/schemas/schema_1' + properties: {} + title: properties + type: object + additionalProperties: + $ref: '#/components/schemas/schema_oneOf_additionalProperties' + description: + title: description + type: string + format: + title: format + type: string + default: + title: default + type: object + nullable: + default: false + title: nullable + type: boolean + readOnly: + default: false + title: readOnly + type: boolean + writeOnly: + default: false + title: writeOnly + type: boolean + example: + title: example + type: object + deprecated: + default: false + title: deprecated + type: boolean + contentMediaType: + title: contentMediaType + type: string + contentEncoding: + title: contentEncoding + type: string + contentSchema: + title: contentSchema + type: string + title: schema_oneOf + type: object + ogcapppkg_executionUnit: + oneOf: + - $ref: '#/components/schemas/executionUnit' + - $ref: '#/components/schemas/link' + - $ref: '#/components/schemas/qualifiedInputValue' + - $ref: '#/components/schemas/ogcapppkg-array' + title: ogcapppkg_executionUnit + GeoJSON_Feature_id: + oneOf: + - type: number + - type: string + title: GeoJSON_Feature_id + GeoJSON_Point: + nullable: true + properties: + type: + enum: + - Point + title: type + type: string + coordinates: + items: + type: number + minItems: 2 + title: coordinates + type: array + bbox: + items: + type: number + minItems: 4 + title: bbox + type: array + required: + - coordinates + - type + title: GeoJSON Point + type: object + GeoJSON_LineString: + properties: + type: + enum: + - LineString + title: type + type: string + coordinates: + items: + items: + type: number + minItems: 2 + type: array + minItems: 2 + title: coordinates + type: array + bbox: + items: + type: number + minItems: 4 + title: bbox + type: array + required: + - coordinates + - type + title: GeoJSON LineString + type: object + GeoJSON_Polygon: + properties: + type: + enum: + - Polygon + title: type + type: string + coordinates: + items: + items: + items: + type: number + minItems: 2 + type: array + minItems: 4 + type: array + title: coordinates + type: array + bbox: + items: + type: number + minItems: 4 + title: bbox + type: array + required: + - coordinates + - type + title: GeoJSON Polygon + type: object + GeoJSON_MultiPoint: + properties: + type: + enum: + - MultiPoint + title: type + type: string + coordinates: + items: + items: + type: number + minItems: 2 + type: array + title: coordinates + type: array + bbox: + items: + type: number + minItems: 4 + title: bbox + type: array + required: + - coordinates + - type + title: GeoJSON MultiPoint + type: object + GeoJSON_MultiLineString: + properties: + type: + enum: + - MultiLineString + title: type + type: string + coordinates: + items: + items: + items: + type: number + minItems: 2 + type: array + minItems: 2 + type: array + title: coordinates + type: array + bbox: + items: + type: number + minItems: 4 + title: bbox + type: array + required: + - coordinates + - type + title: GeoJSON MultiLineString + type: object + GeoJSON_MultiPolygon: + properties: + type: + enum: + - MultiPolygon + title: type + type: string + coordinates: + items: + items: + items: + items: + type: number + minItems: 2 + type: array + minItems: 4 + type: array + type: array + title: coordinates + type: array + bbox: + items: + type: number + minItems: 4 + title: bbox + type: array + required: + - coordinates + - type + title: GeoJSON MultiPolygon + type: object + GeoJSON_Feature_geometry: + oneOf: + - $ref: '#/components/schemas/GeoJSON_Point' + - $ref: '#/components/schemas/GeoJSON_LineString' + - $ref: '#/components/schemas/GeoJSON_Polygon' + - $ref: '#/components/schemas/GeoJSON_MultiPoint' + - $ref: '#/components/schemas/GeoJSON_MultiLineString' + - $ref: '#/components/schemas/GeoJSON_MultiPolygon' + title: GeoJSON_Feature_geometry + GeoJSON_Feature: + properties: + type: + enum: + - Feature + title: type + type: string + id: + $ref: '#/components/schemas/GeoJSON_Feature_id' + properties: + nullable: true + title: properties + type: object + geometry: + $ref: '#/components/schemas/GeoJSON_Feature_geometry' + bbox: + items: + type: number + minItems: 4 + title: bbox + type: array + required: + - geometry + - properties + - type + title: GeoJSON Feature + type: object + executionUnit_config: + additionalProperties: true + description: Hardware requirements and configuration properties for executing + the process. + properties: + cpuMin: + description: Minimum number of CPUs required to run the process (unit is + CPU core). + minimum: 1 + type: number + cpuMax: + description: Maximum number of CPU dedicated to the process (unit is CPU + core) + type: number + memoryMin: + description: Minimum RAM memory required to run the application (unit is + GB) + type: number + memoryMax: + description: Maximum RAM memory dedicated to the application (unit is GB) + type: number + storageTempMin: + description: Minimum required temporary storage size (unit is GB) + type: number + storageOutputsMin: + description: Minimum required output storage size (unit is GB) + type: number + jobTimeout: + description: Timeout delay for a job execution (in seconds) + type: number + title: executionUnit_config + type: object + ogcapppkg_array_inner: + oneOf: + - $ref: '#/components/schemas/executionUnit' + - $ref: '#/components/schemas/link' + - $ref: '#/components/schemas/qualifiedInputValue' + title: ogcapppkg_array_inner + fieldsModifiers_properties: + oneOf: + - additionalProperties: + type: string + type: object + - items: + type: string + type: array + title: fieldsModifiers_properties diff --git a/app-cp/pyproject.toml b/app-cp/pyproject.toml new file mode 100644 index 0000000..c2826df --- /dev/null +++ b/app-cp/pyproject.toml @@ -0,0 +1,30 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.black] +line-length = 88 +exclude = ''' +( + /( + \.eggs # exclude a few common directories in the + | \.git # root of the project + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist + )/ +) +''' + +[tool.isort] +profile = "black" +skip = [ + '.eggs', '.git', '.hg', '.mypy_cache', '.nox', '.pants.d', '.tox', + '.venv', '_build', 'buck-out', 'build', 'dist', 'node_modules', 'venv', +] +skip_gitignore = true diff --git a/app-cp/requirements.txt b/app-cp/requirements.txt new file mode 100644 index 0000000..b3ea81f --- /dev/null +++ b/app-cp/requirements.txt @@ -0,0 +1,36 @@ +aiofiles==23.1.0 +aniso8601==7.0.0 +async-exit-stack==1.0.1 +async-generator==1.10 +certifi==2023.7.22 +chardet==4.0.0 +click==7.1.2 +dnspython==2.6.1 +email-validator==2.0.0 +fastapi==0.109.2 +graphene==2.1.8 +graphql-core==2.3.2 +graphql-relay==2.0.1 +h11==0.12.0 +httptools==0.1.2 +httpx==0.24.1 +idna==3.7 +itsdangerous==1.1.0 +Jinja2==3.1.4 +MarkupSafe==2.0.1 +orjson==3.9.15 +promise==2.3 +pydantic>=2 +python-dotenv==0.17.1 +python-multipart==0.0.7 +PyYAML==5.4.1 +requests==2.32.0 +Rx==1.6.1 +starlette==0.36.3 +typing-extensions==4.8.0 +ujson==4.0.2 +urllib3==1.26.19 +uvicorn==0.13.4 +uvloop==0.19.0 +watchgod==0.7 +websockets==10.0 diff --git a/app-cp/setup.cfg b/app-cp/setup.cfg new file mode 100644 index 0000000..1747ec5 --- /dev/null +++ b/app-cp/setup.cfg @@ -0,0 +1,20 @@ +[metadata] +name = unity_sps_ogc_processes_api +version = 0.1 +description = Example API Definition for OGC API - Processes +long_description = file: README.md +keywords = OpenAPI OGC API - Processes +python_requires = >= 3.7.* +classifiers = + Operating System :: OS Independent + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.7 + +[options] +install_requires = fastapi[all] +setup_requires = setuptools +package_dir = =src +packages = find_namespace: + +[options.packages.find] +where = src diff --git a/app-cp/src/openapi_server/impl/__init__.py b/app-cp/src/openapi_server/impl/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/__init__.py b/app-cp/src/unity_sps_ogc_processes_api/apis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/api_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/api_api.py new file mode 100644 index 0000000..9813300 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/api_api.py @@ -0,0 +1,82 @@ +# coding: utf-8 + +import importlib +import pkgutil +from typing import Dict, List # noqa: F401 + +from fastapi import ( # noqa: F401 + APIRouter, + Body, + Cookie, + Depends, + Form, + Header, + Path, + Query, + Response, + Security, + status, +) + +import openapi_server.impl +from unity_sps_ogc_processes_api.models.enumeration import Enumeration +from unity_sps_ogc_processes_api.models.exception import Exception + +router = APIRouter() + +ns_pkg = openapi_server.impl +for _, name, _ in pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + "."): + importlib.import_module(name) + + +@router.get( + "/api", + responses={ + 200: {"model": object, "description": "The OpenAPI definition of the API."}, + 406: { + "model": Exception, + "description": "Content negotiation failed. For example, the `Accept` header submitted in the request did not support any of the media types supported by the server for the requested resource.", + }, + 500: {"model": Exception, "description": "A server error occurred."}, + }, + tags=["API"], + summary="Retrieve this API definition.", + response_model_by_alias=True, +) +async def get_api( + f: str = Query( + None, + description="The format of the response. If no value is provided, the accept header is used to determine the format. Accepted values are 'json' or 'html'.", + alias="f", + ), +) -> object: ... + + +@router.get( + "/api/processes-list", + responses={ + 200: { + "model": Enumeration, + "description": "An enumerated list of valid string values for API parameters.", + }, + 404: { + "model": Exception, + "description": "The requested resource does not exist on the server. For example, a path parameter had an incorrect value.", + }, + 406: { + "model": Exception, + "description": "Content negotiation failed. For example, the `Accept` header submitted in the request did not support any of the media types supported by the server for the requested resource.", + }, + 500: {"model": Exception, "description": "A server error occurred."}, + }, + tags=["API"], + summary="Retrieve the list of processes available from this API implementation & deployment.", + response_model_by_alias=True, +) +async def get_api_processes( + f: str = Query( + None, + description="The format of the response. If no value is provided, the accept header is used to determine the format. Accepted values are 'json' or 'html'.", + alias="f", + ), +) -> Enumeration: ... diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/api_api_base.py b/app-cp/src/unity_sps_ogc_processes_api/apis/api_api_base.py new file mode 100644 index 0000000..225be12 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/api_api_base.py @@ -0,0 +1,23 @@ +# coding: utf-8 + +from typing import ClassVar, Dict, List, Tuple # noqa: F401 + +from unity_sps_ogc_processes_api.models.enumeration import Enumeration + + +class BaseAPIApi: + subclasses: ClassVar[Tuple] = () + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + BaseAPIApi.subclasses = BaseAPIApi.subclasses + (cls,) + + def get_api( + self, + f: str, + ) -> object: ... + + def get_api_processes( + self, + f: str, + ) -> Enumeration: ... diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api.py new file mode 100644 index 0000000..8db14ff --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api.py @@ -0,0 +1,56 @@ +# coding: utf-8 + +import importlib +import pkgutil +from typing import Dict, List # noqa: F401 + +from fastapi import ( # noqa: F401 + APIRouter, + Body, + Cookie, + Depends, + Form, + Header, + Path, + Query, + Response, + Security, + status, +) + +import openapi_server.impl +from unity_sps_ogc_processes_api.models.conf_classes import ConfClasses +from unity_sps_ogc_processes_api.models.exception import Exception +from unity_sps_ogc_processes_api.models.extra_models import TokenModel # noqa: F401 + +router = APIRouter() + +ns_pkg = openapi_server.impl +for _, name, _ in pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + "."): + importlib.import_module(name) + + +@router.get( + "/conformance", + responses={ + 200: { + "model": ConfClasses, + "description": "The URIs of all conformance classes supported by the server To support \\"generic\\" clients that want to access multiple OGC API - Processes implementations - and not \\"just\\" a specific API / server, the server declares the conformance classes it implements and conforms to.", + }, + 406: { + "model": Exception, + "description": "Content negotiation failed. For example, the `Accept` header submitted in the request did not support any of the media types supported by the server for the requested resource.", + }, + 500: {"model": Exception, "description": "A server error occurred."}, + }, + tags=["Conformance"], + summary="Retrieve the set of OGC API conformance classes that are supported by this service.", + response_model_by_alias=True, +) +async def get_conformance( + f: str = Query( + None, + description="The format of the response. If no value is provided, the accept header is used to determine the format. Accepted values are 'json' or 'html'.", + alias="f", + ), +) -> ConfClasses: ... diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py b/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py new file mode 100644 index 0000000..7373dca --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py @@ -0,0 +1,18 @@ +# coding: utf-8 + +from typing import ClassVar, Dict, List, Tuple # noqa: F401 + +from unity_sps_ogc_processes_api.models.conf_classes import ConfClasses + + +class BaseConformanceApi: + subclasses: ClassVar[Tuple] = () + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + BaseConformanceApi.subclasses = BaseConformanceApi.subclasses + (cls,) + + def get_conformance( + self, + f: str, + ) -> ConfClasses: ... diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py new file mode 100644 index 0000000..abfd6cb --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py @@ -0,0 +1,111 @@ +# coding: utf-8 + +import importlib +import pkgutil +from typing import Dict, List # noqa: F401 + +from fastapi import ( # noqa: F401 + APIRouter, + Body, + Cookie, + Depends, + Form, + Header, + Path, + Query, + Response, + Security, + status, +) + +import openapi_server.impl +from unity_sps_ogc_processes_api.apis.dru_api_base import BaseDRUApi +from unity_sps_ogc_processes_api.models.exception import Exception +from unity_sps_ogc_processes_api.models.extra_models import TokenModel # noqa: F401 +from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg + +router = APIRouter() + +ns_pkg = openapi_server.impl +for _, name, _ in pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + "."): + importlib.import_module(name) + + +@router.post( + "/processes", + responses={ + 201: {"description": ""}, + 403: {"model": Exception, "description": "the processes is not mutable"}, + 409: { + "model": Exception, + "description": "the processes being added is already deployed (i.e. duplicate)", + }, + 500: {"model": Exception, "description": "A server error occurred."}, + }, + tags=["DRU"], + summary="deploy a process.", + response_model_by_alias=True, +) +async def deploy( + ogcapppkg: Ogcapppkg = Body( + None, description="An OGC Application Package used to deploy a new process." + ), + w: str = Query( + None, + description="Point to the workflow identifier for deploying a CWL containing multiple workflow definitions", + alias="w", + ), +) -> None: + """Deploys a process. For more information, see [Section 6.3](http://docs.ogc.org/DRAFTS/20-044.html#_87a6983e-d060-458c-95ab-27e232e64822).""" + return BaseDRUApi.subclasses[0]().deploy(ogcapppkg, w) + + +@router.put( + "/processes/{processId}", + responses={ + 204: {"description": "successful operation (no response body)"}, + 403: {"model": Exception, "description": "the processes is not mutable"}, + 404: { + "model": Exception, + "description": "The requested resource does not exist on the server. For example, a path parameter had an incorrect value.", + }, + 409: { + "model": Exception, + "description": "the processes being added is already deployed (i.e. duplicate)", + }, + 500: {"model": Exception, "description": "A server error occurred."}, + }, + tags=["DRU"], + summary="replace a process.", + response_model_by_alias=True, +) +async def replace( + processId: str = Path(..., description=""), + ogcapppkg: Ogcapppkg = Body( + None, description="An OGC Application Package used to deploy a new process." + ), +) -> None: + """Replaces a process. For more information, see [Section 6.4](http://docs.ogc.org/DRAFTS/20-044.html#_18582f42-ebc6-4284-9333-c089068f62b6).""" + return BaseDRUApi.subclasses[0]().replace(processId, ogcapppkg) + + +@router.delete( + "/processes/{processId}", + responses={ + 204: {"description": "successful operation (no response body)"}, + 403: {"model": Exception, "description": "the processes is not mutable"}, + 404: { + "model": Exception, + "description": "The requested resource does not exist on the server. For example, a path parameter had an incorrect value.", + }, + 500: {"model": Exception, "description": "A server error occurred."}, + }, + tags=["DRU"], + summary="undeploy a process.", + response_model_by_alias=True, +) +async def undeploy( + processId: str = Path(..., description=""), +) -> None: + """Undeploys a process. For more information, see [Section 6.5](http://docs.ogc.org/DRAFTS/20-044.html#_16391f9e-538f-4a84-9710-72a6bab82842).""" + return BaseDRUApi.subclasses[0]().undeploy(processId) diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api_base.py b/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api_base.py new file mode 100644 index 0000000..c84af42 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api_base.py @@ -0,0 +1,36 @@ +# coding: utf-8 + +from typing import ClassVar, Dict, List, Tuple # noqa: F401 + +from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg + + +class BaseDRUApi: + subclasses: ClassVar[Tuple] = () + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + BaseDRUApi.subclasses = BaseDRUApi.subclasses + (cls,) + + def deploy( + self, + ogcapppkg: Ogcapppkg, + w: str, + ) -> None: + """Deploys a process. For more information, see [Section 6.3](http://docs.ogc.org/DRAFTS/20-044.html#_87a6983e-d060-458c-95ab-27e232e64822).""" + ... + + def replace( + self, + processId: str, + ogcapppkg: Ogcapppkg, + ) -> None: + """Replaces a process. For more information, see [Section 6.4](http://docs.ogc.org/DRAFTS/20-044.html#_18582f42-ebc6-4284-9333-c089068f62b6).""" + ... + + def undeploy( + self, + processId: str, + ) -> None: + """Undeploys a process. For more information, see [Section 6.5](http://docs.ogc.org/DRAFTS/20-044.html#_16391f9e-538f-4a84-9710-72a6bab82842).""" + ... diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api.py new file mode 100644 index 0000000..d20d04b --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api.py @@ -0,0 +1,121 @@ +# coding: utf-8 + +import importlib +import pkgutil +from typing import Dict, List # noqa: F401 + +from fastapi import ( # noqa: F401 + APIRouter, + Body, + Cookie, + Depends, + Form, + Header, + Path, + Query, + Response, + Security, + status, +) + +import openapi_server.impl +from unity_sps_ogc_processes_api.apis.jobs_api_base import BaseJobsApi +from unity_sps_ogc_processes_api.models.exception import Exception +from unity_sps_ogc_processes_api.models.extra_models import TokenModel # noqa: F401 +from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData +from unity_sps_ogc_processes_api.models.job_list import JobList +from unity_sps_ogc_processes_api.models.status_info import StatusInfo + +router = APIRouter() + +ns_pkg = openapi_server.impl +for _, name, _ in pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + "."): + importlib.import_module(name) + + +@router.delete( + "/jobs/{jobId}", + responses={ + 200: {"model": StatusInfo, "description": "The status of a job."}, + 404: { + "model": Exception, + "description": "The requested resource does not exist on the server. For example, a path parameter had an incorrect value.", + }, + 500: {"model": Exception, "description": "A server error occurred."}, + }, + tags=["Jobs"], + summary="cancel a job execution, remove a finished job", + response_model_by_alias=True, +) +async def dismiss( + jobId: str = Path(..., description="local identifier of a job"), +) -> StatusInfo: + """Cancel a job execution and remove it from the jobs list. For more information, see [Section 14]https://docs.ogc.org/is/18-062r2/18-062r2.html#Dismiss).""" + return BaseJobsApi.subclasses[0]().dismiss(jobId) + + +@router.get( + "/jobs", + responses={ + 200: {"model": JobList, "description": "A list of jobs for this process."}, + 404: { + "model": Exception, + "description": "The requested resource does not exist on the server. For example, a path parameter had an incorrect value.", + }, + }, + tags=["Jobs"], + summary="retrieve the list of jobs.", + response_model_by_alias=True, +) +async def get_jobs() -> JobList: + """Lists available jobs. For more information, see [Section 12](https://docs.ogc.org/is/18-062r2/18-062r2.html#Job_list).""" + return BaseJobsApi.subclasses[0]().get_jobs() + + +@router.get( + "/jobs/{jobId}/results", + responses={ + 200: { + "model": Dict[str, InlineOrRefData], + "description": "The processing results of a job.", + }, + 404: { + "model": Exception, + "description": "The requested resource does not exist on the server. For example, a path parameter had an incorrect value.", + }, + 500: {"model": Exception, "description": "A server error occurred."}, + }, + tags=["Jobs"], + summary="retrieve the result(s) of a job", + response_model_by_alias=True, +) +async def get_result( + jobId: str = Path(..., description="local identifier of a job"), + prefer: str = Header( + None, + description="Indicates client preferences, such as whether the client wishes a self-contained or minimal response. A `return=minimal` preference indicates that the client would prefer that links be returned to larger object to minimize the response payload. A `return=representation` indicates that the client would prefer if the server can return a self-contained response.", + ), +) -> Dict[str, InlineOrRefData]: + """Lists available results of a job. In case of a failure, lists exceptions instead. For more information, see [Section 7.11](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_retrieve_job_results).""" + return BaseJobsApi.subclasses[0]().get_result(jobId, prefer) + + +@router.get( + "/jobs/{jobId}", + responses={ + 200: {"model": StatusInfo, "description": "The status of a job."}, + 404: { + "model": Exception, + "description": "The requested resource does not exist on the server. For example, a path parameter had an incorrect value.", + }, + 500: {"model": Exception, "description": "A server error occurred."}, + }, + tags=["Jobs"], + summary="retrieve the status of a job", + response_model_by_alias=True, +) +async def get_status( + jobId: str = Path(..., description="local identifier of a job"), +) -> StatusInfo: + """Shows the status of a job. For more information, see [Section 7.10](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_retrieve_status_info).""" + return BaseJobsApi.subclasses[0]().get_status(jobId) diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py b/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py new file mode 100644 index 0000000..acfe79d --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py @@ -0,0 +1,43 @@ +# coding: utf-8 + +from typing import ClassVar, Dict, List, Tuple # noqa: F401 + +from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData +from unity_sps_ogc_processes_api.models.job_list import JobList +from unity_sps_ogc_processes_api.models.status_info import StatusInfo + + +class BaseJobsApi: + subclasses: ClassVar[Tuple] = () + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + BaseJobsApi.subclasses = BaseJobsApi.subclasses + (cls,) + + def dismiss( + self, + jobId: str, + ) -> StatusInfo: + """Cancel a job execution and remove it from the jobs list. For more information, see [Section 14]https://docs.ogc.org/is/18-062r2/18-062r2.html#Dismiss).""" + ... + + def get_jobs( + self, + ) -> JobList: + """Lists available jobs. For more information, see [Section 12](https://docs.ogc.org/is/18-062r2/18-062r2.html#Job_list).""" + ... + + def get_result( + self, + jobId: str, + prefer: str, + ) -> Dict[str, InlineOrRefData]: + """Lists available results of a job. In case of a failure, lists exceptions instead. For more information, see [Section 7.11](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_retrieve_job_results).""" + ... + + def get_status( + self, + jobId: str, + ) -> StatusInfo: + """Shows the status of a job. For more information, see [Section 7.10](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_retrieve_status_info).""" + ... diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api.py new file mode 100644 index 0000000..7bafb52 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api.py @@ -0,0 +1,56 @@ +# coding: utf-8 + +import importlib +import pkgutil +from typing import Dict, List # noqa: F401 + +from fastapi import ( # noqa: F401 + APIRouter, + Body, + Cookie, + Depends, + Form, + Header, + Path, + Query, + Response, + Security, + status, +) + +import openapi_server.impl +from unity_sps_ogc_processes_api.models.exception import Exception +from unity_sps_ogc_processes_api.models.extra_models import TokenModel # noqa: F401 +from unity_sps_ogc_processes_api.models.landing_page import LandingPage + +router = APIRouter() + +ns_pkg = openapi_server.impl +for _, name, _ in pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + "."): + importlib.import_module(name) + + +@router.get( + "/", + responses={ + 200: { + "model": LandingPage, + "description": "The landing page provides links to the API definition (link relation `service-desc`, in this case path `/api`), to the Conformance declaration (path `/conformance`, link relation `http://www.opengis.net/def/rel/ogc/1.0/conformance`), and to other resources.", + }, + 406: { + "model": Exception, + "description": "Content negotiation failed. For example, the `Accept` header submitted in the request did not support any of the media types supported by the server for the requested resource.", + }, + 500: {"model": Exception, "description": "A server error occurred."}, + }, + tags=["Landing Page"], + summary="Retrieve the OGC API landing page for this service.", + response_model_by_alias=True, +) +async def get_landing_page( + f: str = Query( + None, + description="The format of the response. If no value is provided, the accept header is used to determine the format. Accepted values are 'json' or 'html'.", + alias="f", + ), +) -> LandingPage: ... diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api_base.py b/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api_base.py new file mode 100644 index 0000000..d389c11 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api_base.py @@ -0,0 +1,18 @@ +# coding: utf-8 + +from typing import ClassVar, Dict, List, Tuple # noqa: F401 + +from unity_sps_ogc_processes_api.models.landing_page import LandingPage + + +class BaseLandingPageApi: + subclasses: ClassVar[Tuple] = () + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + BaseLandingPageApi.subclasses = BaseLandingPageApi.subclasses + (cls,) + + def get_landing_page( + self, + f: str, + ) -> LandingPage: ... diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api.py new file mode 100644 index 0000000..3bddd1a --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api.py @@ -0,0 +1,118 @@ +# coding: utf-8 + +import importlib +import pkgutil +from typing import Dict, List # noqa: F401 + +from fastapi import ( # noqa: F401 + APIRouter, + Body, + Cookie, + Depends, + Form, + Header, + Path, + Query, + Response, + Security, + status, +) + +import openapi_server.impl +from unity_sps_ogc_processes_api.apis.processes_api_base import BaseProcessesApi +from unity_sps_ogc_processes_api.models.exception import Exception +from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response +from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows +from unity_sps_ogc_processes_api.models.extra_models import TokenModel # noqa: F401 +from unity_sps_ogc_processes_api.models.process import Process +from unity_sps_ogc_processes_api.models.process_list import ProcessList +from unity_sps_ogc_processes_api.models.status_info import StatusInfo + +router = APIRouter() + +ns_pkg = openapi_server.impl +for _, name, _ in pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + "."): + importlib.import_module(name) + + +@router.post( + "/processes/{processId}/execution", + responses={ + 200: { + "model": Execute200Response, + "description": "Result of synchronous execution", + }, + 201: { + "model": StatusInfo, + "description": "Started asynchronous execution. Created job.", + }, + 303: { + "description": "For _Collection Output_ execution, redirection to an OGC API landing page or collection." + }, + 404: { + "model": Exception, + "description": "The requested resource does not exist on the server. For example, a path parameter had an incorrect value.", + }, + 500: {"model": Exception, "description": "A server error occurred."}, + }, + tags=["Processes"], + summary="execute a process.", + response_model_by_alias=True, +) +async def execute( + processId: str = Path(..., description=""), + execute_workflows: ExecuteWorkflows = Body( + None, + description="An execution request specifying any inputs for the process to execute, and optionally to select specific outputs.", + ), + response: str = Query( + None, + description="For executing the process using the _Collection Output_ mechanism, where the client is redirected (_303_ status) to either an OGC API landing page or collection resource, from which one or more OGC API data access mechanism is available. Data access requests may trigger processing on-demand for a given area, time and resolution of interest.", + alias="response", + ), + prefer: str = Header( + None, + description="Indicates client preferences, including whether the client is capable of asynchronous processing. A `respond-async` preference indicates a preference for asynchronous processing. A `wait: <x>s` preference indicates that the client prefers to wait up to x seconds to receive a reponse synchronously before the server falls back to asynchronous processing.", + ), +) -> Execute200Response: + """Executes a process (this may result in the creation of a job resource e.g., for _asynchronous execution_). For more information, see [Section 7.9](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_create_job).""" + return BaseProcessesApi.subclasses[0]().execute( + processId, execute_workflows, response, prefer + ) + + +@router.get( + "/processes/{processId}", + responses={ + 200: {"model": Process, "description": "A process description."}, + 404: { + "model": Exception, + "description": "The requested resource does not exist on the server. For example, a path parameter had an incorrect value.", + }, + }, + tags=["Processes"], + summary="retrieve a process description", + response_model_by_alias=True, +) +async def get_process_description( + processId: str = Path(..., description=""), +) -> Process: + """The process description contains information about inputs and outputs and a link to the execution-endpoint for the process. The Core does not mandate the use of a specific process description to specify the interface of a process. That said, the Core requirements class makes the following recommendation: Implementations SHOULD consider supporting the OGC process description. For more information, see [Section 7.8](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_process_description).""" + return BaseProcessesApi.subclasses[0]().get_process_description(processId) + + +@router.get( + "/processes", + responses={ + 200: { + "model": ProcessList, + "description": "Information about the available processes", + }, + }, + tags=["Processes"], + summary="retrieve the list of available processes", + response_model_by_alias=True, +) +async def get_processes() -> ProcessList: + """The list of processes contains a summary of each process the OGC API - Processes offers, including the link to a more detailed description of the process. For more information, see [Section 7.7]https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_process_list).""" + return BaseProcessesApi.subclasses[0]().get_processes() diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api_base.py b/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api_base.py new file mode 100644 index 0000000..6230461 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api_base.py @@ -0,0 +1,39 @@ +# coding: utf-8 + +from typing import ClassVar, Dict, List, Tuple # noqa: F401 + +from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response +from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows +from unity_sps_ogc_processes_api.models.process import Process +from unity_sps_ogc_processes_api.models.process_list import ProcessList + + +class BaseProcessesApi: + subclasses: ClassVar[Tuple] = () + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + BaseProcessesApi.subclasses = BaseProcessesApi.subclasses + (cls,) + + def execute( + self, + processId: str, + execute_workflows: ExecuteWorkflows, + response: str, + prefer: str, + ) -> Execute200Response: + """Executes a process (this may result in the creation of a job resource e.g., for _asynchronous execution_). For more information, see [Section 7.9](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_create_job).""" + ... + + def get_process_description( + self, + processId: str, + ) -> Process: + """The process description contains information about inputs and outputs and a link to the execution-endpoint for the process. The Core does not mandate the use of a specific process description to specify the interface of a process. That said, the Core requirements class makes the following recommendation: Implementations SHOULD consider supporting the OGC process description. For more information, see [Section 7.8](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_process_description).""" + ... + + def get_processes( + self, + ) -> ProcessList: + """The list of processes contains a summary of each process the OGC API - Processes offers, including the link to a more detailed description of the process. For more information, see [Section 7.7]https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_process_list).""" + ... diff --git a/app-cp/src/unity_sps_ogc_processes_api/main.py b/app-cp/src/unity_sps_ogc_processes_api/main.py new file mode 100644 index 0000000..2693fd6 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/main.py @@ -0,0 +1,40 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from fastapi import FastAPI + +from unity_sps_ogc_processes_api.apis.api_api import router as APIApiRouter +from unity_sps_ogc_processes_api.apis.conformance_api import ( + router as ConformanceApiRouter, +) +from unity_sps_ogc_processes_api.apis.dru_api import router as DRUApiRouter +from unity_sps_ogc_processes_api.apis.jobs_api import router as JobsApiRouter +from unity_sps_ogc_processes_api.apis.landing_page_api import ( + router as LandingPageApiRouter, +) +from unity_sps_ogc_processes_api.apis.processes_api import router as ProcessesApiRouter + +app = FastAPI( + title="OGC API - Processes", + description="Example API Definition for OGC API - Processes", + version="0.1", +) + +app.include_router(APIApiRouter) +app.include_router(ConformanceApiRouter) +app.include_router(DRUApiRouter) +app.include_router(JobsApiRouter) +app.include_router(LandingPageApiRouter) +app.include_router(ProcessesApiRouter) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/__init__.py b/app-cp/src/unity_sps_ogc_processes_api/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/bbox.py b/app-cp/src/unity_sps_ogc_processes_api/models/bbox.py new file mode 100644 index 0000000..2c49ce6 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/bbox.py @@ -0,0 +1,101 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import BaseModel, StrictFloat, StrictInt + +from unity_sps_ogc_processes_api.models.bbox_def_crs import BboxDefCrs + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Bbox(BaseModel): + """ + Bbox + """ # noqa: E501 + + bbox: List[Union[StrictFloat, StrictInt]] + crs: Optional[BboxDefCrs] = None + __properties: ClassVar[List[str]] = ["bbox", "crs"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of Bbox from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of crs + if self.crs: + _dict["crs"] = self.crs.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of Bbox from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "bbox": obj.get("bbox"), + "crs": ( + BboxDefCrs.from_dict(obj.get("crs")) + if obj.get("crs") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/bbox1.py b/app-cp/src/unity_sps_ogc_processes_api/models/bbox1.py new file mode 100644 index 0000000..c85ff66 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/bbox1.py @@ -0,0 +1,101 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import BaseModel, StrictFloat, StrictInt + +from unity_sps_ogc_processes_api.models.bbox_def_crs import BboxDefCrs + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Bbox1(BaseModel): + """ + Bbox1 + """ # noqa: E501 + + bbox: List[Union[StrictFloat, StrictInt]] + crs: Optional[BboxDefCrs] = None + __properties: ClassVar[List[str]] = ["bbox", "crs"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of Bbox1 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of crs + if self.crs: + _dict["crs"] = self.crs.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of Bbox1 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "bbox": obj.get("bbox"), + "crs": ( + BboxDefCrs.from_dict(obj.get("crs")) + if obj.get("crs") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py b/app-cp/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py new file mode 100644 index 0000000..9c415b7 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py @@ -0,0 +1,158 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from pydantic import BaseModel, StrictStr, ValidationError, field_validator +from typing_extensions import Literal + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +BBOXDEFCRS_ANY_OF_SCHEMAS = ["str"] + + +class BboxDefCrs(BaseModel): + """ + BboxDefCrs + """ + + # data type: str + anyof_schema_1_validator: Optional[StrictStr] = ( + "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + ) + # data type: str + anyof_schema_2_validator: Optional[StrictStr] = ( + "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + ) + if TYPE_CHECKING: + actual_instance: Optional[Union[str]] = None + else: + actual_instance: Any = None + any_of_schemas: List[str] = Literal[BBOXDEFCRS_ANY_OF_SCHEMAS] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_anyof(cls, v): + instance = BboxDefCrs.model_construct() + error_messages = [] + # validate data type: str + try: + instance.anyof_schema_1_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: str + try: + instance.anyof_schema_2_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if error_messages: + # no match + raise ValueError( + "No match found when setting the actual_instance in BboxDefCrs with anyOf schemas: str. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + # deserialize data into str + try: + # validation + instance.anyof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_1_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into str + try: + # validation + instance.anyof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_2_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if error_messages: + # no match + raise ValueError( + "No match found when deserializing the JSON string into BboxDefCrs with anyOf schemas: str. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_dict() + else: + return json.dumps(self.actual_instance) + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/bbox_processes.py b/app-cp/src/unity_sps_ogc_processes_api/models/bbox_processes.py new file mode 100644 index 0000000..0f5a692 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/bbox_processes.py @@ -0,0 +1,101 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import BaseModel, StrictFloat, StrictInt + +from unity_sps_ogc_processes_api.models.bbox_def_crs import BboxDefCrs + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class BboxProcesses(BaseModel): + """ + BboxProcesses + """ # noqa: E501 + + bbox: List[Union[StrictFloat, StrictInt]] + crs: Optional[BboxDefCrs] = None + __properties: ClassVar[List[str]] = ["bbox", "crs"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of BboxProcesses from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of crs + if self.crs: + _dict["crs"] = self.crs.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of BboxProcesses from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "bbox": obj.get("bbox"), + "crs": ( + BboxDefCrs.from_dict(obj.get("crs")) + if obj.get("crs") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/collection_info.py b/app-cp/src/unity_sps_ogc_processes_api/models/collection_info.py new file mode 100644 index 0000000..72bece4 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/collection_info.py @@ -0,0 +1,199 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr +from typing_extensions import Annotated + +from unity_sps_ogc_processes_api.models.collection_info_data_type import ( + CollectionInfoDataType, +) +from unity_sps_ogc_processes_api.models.extent_uad import ExtentUad +from unity_sps_ogc_processes_api.models.link import Link + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class CollectionInfo(BaseModel): + """ + CollectionInfo + """ # noqa: E501 + + id: StrictStr = Field( + description="identifier of the collection used, for example, in URIs" + ) + title: Optional[StrictStr] = Field( + default=None, description="human readable title of the collection" + ) + description: Optional[StrictStr] = Field( + default=None, description="a description of the data in the collection" + ) + links: List[Link] + extent: Optional[ExtentUad] = None + item_type: Optional[StrictStr] = Field( + default="unknown", + description="indicator about the type of the items in the collection if the collection has an accessible /collections/{collectionId}/items endpoint", + alias="itemType", + ) + crs: Optional[List[StrictStr]] = Field( + default=None, + description="the list of coordinate reference systems supported by the API; the first item is the default coordinate reference system", + ) + data_type: Optional[CollectionInfoDataType] = Field(default=None, alias="dataType") + geometry_dimension: Optional[Annotated[int, Field(le=3, strict=True, ge=0)]] = ( + Field( + default=None, + description="The geometry dimension of the features shown in this layer (0: points, 1: curves, 2: surfaces, 3: solids), unspecified: mixed or unknown", + alias="geometryDimension", + ) + ) + min_scale_denominator: Optional[Union[StrictFloat, StrictInt]] = Field( + default=None, + description="Minimum scale denominator for usage of the collection", + alias="minScaleDenominator", + ) + max_scale_denominator: Optional[Union[StrictFloat, StrictInt]] = Field( + default=None, + description="Maximum scale denominator for usage of the collection", + alias="maxScaleDenominator", + ) + min_cell_size: Optional[Union[StrictFloat, StrictInt]] = Field( + default=None, + description="Minimum cell size for usage of the collection", + alias="minCellSize", + ) + max_cell_size: Optional[Union[StrictFloat, StrictInt]] = Field( + default=None, + description="Maximum cell size for usage of the collection", + alias="maxCellSize", + ) + __properties: ClassVar[List[str]] = [ + "id", + "title", + "description", + "links", + "extent", + "itemType", + "crs", + "dataType", + "geometryDimension", + "minScaleDenominator", + "maxScaleDenominator", + "minCellSize", + "maxCellSize", + ] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of CollectionInfo from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in links (list) + _items = [] + if self.links: + for _item in self.links: + if _item: + _items.append(_item.to_dict()) + _dict["links"] = _items + # override the default output from pydantic by calling `to_dict()` of extent + if self.extent: + _dict["extent"] = self.extent.to_dict() + # override the default output from pydantic by calling `to_dict()` of data_type + if self.data_type: + _dict["dataType"] = self.data_type.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of CollectionInfo from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "id": obj.get("id"), + "title": obj.get("title"), + "description": obj.get("description"), + "links": ( + [Link.from_dict(_item) for _item in obj.get("links")] + if obj.get("links") is not None + else None + ), + "extent": ( + ExtentUad.from_dict(obj.get("extent")) + if obj.get("extent") is not None + else None + ), + "itemType": ( + obj.get("itemType") + if obj.get("itemType") is not None + else "unknown" + ), + "crs": obj.get("crs"), + "dataType": ( + CollectionInfoDataType.from_dict(obj.get("dataType")) + if obj.get("dataType") is not None + else None + ), + "geometryDimension": obj.get("geometryDimension"), + "minScaleDenominator": obj.get("minScaleDenominator"), + "maxScaleDenominator": obj.get("maxScaleDenominator"), + "minCellSize": obj.get("minCellSize"), + "maxCellSize": obj.get("maxCellSize"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/collection_info_data_type.py b/app-cp/src/unity_sps_ogc_processes_api/models/collection_info_data_type.py new file mode 100644 index 0000000..028fc9d --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/collection_info_data_type.py @@ -0,0 +1,85 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List + +from pydantic import BaseModel + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class CollectionInfoDataType(BaseModel): + """ + CollectionInfoDataType + """ # noqa: E501 + + __properties: ClassVar[List[str]] = [] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of CollectionInfoDataType from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of CollectionInfoDataType from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({}) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/collections.py b/app-cp/src/unity_sps_ogc_processes_api/models/collections.py new file mode 100644 index 0000000..4dd6775 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/collections.py @@ -0,0 +1,138 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from datetime import datetime +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field +from typing_extensions import Annotated + +from unity_sps_ogc_processes_api.models.collection_info import CollectionInfo +from unity_sps_ogc_processes_api.models.link import Link + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Collections(BaseModel): + """ + Collections + """ # noqa: E501 + + links: List[Link] + time_stamp: Optional[datetime] = Field(default=None, alias="timeStamp") + number_matched: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( + default=None, alias="numberMatched" + ) + number_returned: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( + default=None, alias="numberReturned" + ) + collections: List[CollectionInfo] + __properties: ClassVar[List[str]] = [ + "links", + "timeStamp", + "numberMatched", + "numberReturned", + "collections", + ] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of Collections from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in links (list) + _items = [] + if self.links: + for _item in self.links: + if _item: + _items.append(_item.to_dict()) + _dict["links"] = _items + # override the default output from pydantic by calling `to_dict()` of each item in collections (list) + _items = [] + if self.collections: + for _item in self.collections: + if _item: + _items.append(_item.to_dict()) + _dict["collections"] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of Collections from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "links": ( + [Link.from_dict(_item) for _item in obj.get("links")] + if obj.get("links") is not None + else None + ), + "timeStamp": obj.get("timeStamp"), + "numberMatched": obj.get("numberMatched"), + "numberReturned": obj.get("numberReturned"), + "collections": ( + [ + CollectionInfo.from_dict(_item) + for _item in obj.get("collections") + ] + if obj.get("collections") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/conf_classes.py b/app-cp/src/unity_sps_ogc_processes_api/models/conf_classes.py new file mode 100644 index 0000000..09ac3da --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/conf_classes.py @@ -0,0 +1,86 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List + +from pydantic import BaseModel, Field, StrictStr + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class ConfClasses(BaseModel): + """ + ConfClasses + """ # noqa: E501 + + conforms_to: List[StrictStr] = Field(alias="conformsTo") + __properties: ClassVar[List[str]] = ["conformsTo"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ConfClasses from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of ConfClasses from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({"conformsTo": obj.get("conformsTo")}) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/crs.py b/app-cp/src/unity_sps_ogc_processes_api/models/crs.py new file mode 100644 index 0000000..47a3fab --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/crs.py @@ -0,0 +1,168 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.crs_one_of import CrsOneOf + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +CRS_ONE_OF_SCHEMAS = ["CrsOneOf", "str"] + + +class Crs(BaseModel): + """ + Crs + """ + + # data type: str + oneof_schema_1_validator: Optional[StrictStr] = Field( + default=None, + description="Simplification of the object into a url if the other properties are not present", + ) + # data type: CrsOneOf + oneof_schema_2_validator: Optional[CrsOneOf] = None + actual_instance: Optional[Union[CrsOneOf, str]] = None + one_of_schemas: List[str] = Literal["CrsOneOf", "str"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = Crs.model_construct() + error_messages = [] + match = 0 + # validate data type: str + try: + instance.oneof_schema_1_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: CrsOneOf + if not isinstance(v, CrsOneOf): + error_messages.append(f"Error! Input type `{type(v)}` is not `CrsOneOf`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in Crs with oneOf schemas: CrsOneOf, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in Crs with oneOf schemas: CrsOneOf, str. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into str + try: + # validation + instance.oneof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_1_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into CrsOneOf + try: + instance.actual_instance = CrsOneOf.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into Crs with oneOf schemas: CrsOneOf, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into Crs with oneOf schemas: CrsOneOf, str. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of.py b/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of.py new file mode 100644 index 0000000..e059e18 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of.py @@ -0,0 +1,186 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.crs_one_of_one_of import CrsOneOfOneOf +from unity_sps_ogc_processes_api.models.crs_one_of_one_of1 import CrsOneOfOneOf1 +from unity_sps_ogc_processes_api.models.crs_one_of_one_of2 import CrsOneOfOneOf2 + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +CRSONEOF_ONE_OF_SCHEMAS = ["CrsOneOfOneOf", "CrsOneOfOneOf1", "CrsOneOfOneOf2"] + + +class CrsOneOf(BaseModel): + """ + CrsOneOf + """ + + # data type: CrsOneOfOneOf + oneof_schema_1_validator: Optional[CrsOneOfOneOf] = None + # data type: CrsOneOfOneOf1 + oneof_schema_2_validator: Optional[CrsOneOfOneOf1] = None + # data type: CrsOneOfOneOf2 + oneof_schema_3_validator: Optional[CrsOneOfOneOf2] = None + actual_instance: Optional[Union[CrsOneOfOneOf, CrsOneOfOneOf1, CrsOneOfOneOf2]] = ( + None + ) + one_of_schemas: List[str] = Literal[ + "CrsOneOfOneOf", "CrsOneOfOneOf1", "CrsOneOfOneOf2" + ] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + CrsOneOf.model_construct() + error_messages = [] + match = 0 + # validate data type: CrsOneOfOneOf + if not isinstance(v, CrsOneOfOneOf): + error_messages.append( + f"Error! Input type `{type(v)}` is not `CrsOneOfOneOf`" + ) + else: + match += 1 + # validate data type: CrsOneOfOneOf1 + if not isinstance(v, CrsOneOfOneOf1): + error_messages.append( + f"Error! Input type `{type(v)}` is not `CrsOneOfOneOf1`" + ) + else: + match += 1 + # validate data type: CrsOneOfOneOf2 + if not isinstance(v, CrsOneOfOneOf2): + error_messages.append( + f"Error! Input type `{type(v)}` is not `CrsOneOfOneOf2`" + ) + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in CrsOneOf with oneOf schemas: CrsOneOfOneOf, CrsOneOfOneOf1, CrsOneOfOneOf2. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in CrsOneOf with oneOf schemas: CrsOneOfOneOf, CrsOneOfOneOf1, CrsOneOfOneOf2. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into CrsOneOfOneOf + try: + instance.actual_instance = CrsOneOfOneOf.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into CrsOneOfOneOf1 + try: + instance.actual_instance = CrsOneOfOneOf1.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into CrsOneOfOneOf2 + try: + instance.actual_instance = CrsOneOfOneOf2.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into CrsOneOf with oneOf schemas: CrsOneOfOneOf, CrsOneOfOneOf1, CrsOneOfOneOf2. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into CrsOneOf with oneOf schemas: CrsOneOfOneOf, CrsOneOfOneOf1, CrsOneOfOneOf2. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py b/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py new file mode 100644 index 0000000..bb018ae --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py @@ -0,0 +1,88 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List + +from pydantic import BaseModel, Field, StrictStr + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class CrsOneOfOneOf(BaseModel): + """ + CrsOneOfOneOf + """ # noqa: E501 + + uri: StrictStr = Field( + description="Reference to one coordinate reference system (CRS)" + ) + __properties: ClassVar[List[str]] = ["uri"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of CrsOneOfOneOf from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of CrsOneOfOneOf from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({"uri": obj.get("uri")}) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of1.py b/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of1.py new file mode 100644 index 0000000..067c724 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of1.py @@ -0,0 +1,86 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List + +from pydantic import BaseModel + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class CrsOneOfOneOf1(BaseModel): + """ + CrsOneOfOneOf1 + """ # noqa: E501 + + wkt: Any + __properties: ClassVar[List[str]] = ["wkt"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of CrsOneOfOneOf1 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of CrsOneOfOneOf1 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({"wkt": obj.get("wkt")}) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of2.py b/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of2.py new file mode 100644 index 0000000..927e4a9 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of2.py @@ -0,0 +1,89 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List + +from pydantic import BaseModel, Field + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class CrsOneOfOneOf2(BaseModel): + """ + CrsOneOfOneOf2 + """ # noqa: E501 + + reference_system: Dict[str, Any] = Field( + description="A reference system data structure as defined in the MD_ReferenceSystem of the ISO 19115", + alias="referenceSystem", + ) + __properties: ClassVar[List[str]] = ["referenceSystem"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of CrsOneOfOneOf2 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of CrsOneOfOneOf2 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({"referenceSystem": obj.get("referenceSystem")}) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/data_type.py b/app-cp/src/unity_sps_ogc_processes_api/models/data_type.py new file mode 100644 index 0000000..c530760 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/data_type.py @@ -0,0 +1,167 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, StrictStr, ValidationError, field_validator +from typing_extensions import Literal + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +DATATYPE_ONE_OF_SCHEMAS = ["str"] + + +class DataType(BaseModel): + """ + DataType + """ + + # data type: str + oneof_schema_1_validator: Optional[StrictStr] = None + # data type: str + oneof_schema_2_validator: Optional[StrictStr] = None + actual_instance: Optional[Union[str]] = None + one_of_schemas: List[str] = Literal["str"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = DataType.model_construct() + error_messages = [] + match = 0 + # validate data type: str + try: + instance.oneof_schema_1_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: str + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in DataType with oneOf schemas: str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in DataType with oneOf schemas: str. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into str + try: + # validation + instance.oneof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_1_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into str + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into DataType with oneOf schemas: str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into DataType with oneOf schemas: str. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/description_type.py b/app-cp/src/unity_sps_ogc_processes_api/models/description_type.py new file mode 100644 index 0000000..305fecf --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/description_type.py @@ -0,0 +1,109 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, StrictStr + +from unity_sps_ogc_processes_api.models.metadata import Metadata + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class DescriptionType(BaseModel): + """ + DescriptionType + """ # noqa: E501 + + title: Optional[StrictStr] = None + description: Optional[StrictStr] = None + keywords: Optional[List[StrictStr]] = None + metadata: Optional[List[Metadata]] = None + __properties: ClassVar[List[str]] = ["title", "description", "keywords", "metadata"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of DescriptionType from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in metadata (list) + _items = [] + if self.metadata: + for _item in self.metadata: + if _item: + _items.append(_item.to_dict()) + _dict["metadata"] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of DescriptionType from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "title": obj.get("title"), + "description": obj.get("description"), + "keywords": obj.get("keywords"), + "metadata": ( + [Metadata.from_dict(_item) for _item in obj.get("metadata")] + if obj.get("metadata") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/enumeration.py b/app-cp/src/unity_sps_ogc_processes_api/models/enumeration.py new file mode 100644 index 0000000..bebfa57 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/enumeration.py @@ -0,0 +1,94 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List + +from pydantic import BaseModel, StrictStr, field_validator + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Enumeration(BaseModel): + """ + Enumeration + """ # noqa: E501 + + type: StrictStr + enum: List[StrictStr] + __properties: ClassVar[List[str]] = ["type", "enum"] + + @field_validator("type") + def type_validate_enum(cls, value): + """Validates the enum""" + if value not in ("enum"): + raise ValueError("must be one of enum values ('enum')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of Enumeration from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of Enumeration from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({"type": obj.get("type"), "enum": obj.get("enum")}) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/exception.py b/app-cp/src/unity_sps_ogc_processes_api/models/exception.py new file mode 100644 index 0000000..18d4451 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/exception.py @@ -0,0 +1,118 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, StrictInt, StrictStr + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Exception(BaseModel): + """ + JSON schema for exceptions based on RFC 7807 + """ # noqa: E501 + + type: StrictStr + title: Optional[StrictStr] = None + status: Optional[StrictInt] = None + detail: Optional[StrictStr] = None + instance: Optional[StrictStr] = None + additional_properties: Dict[str, Any] = {} + __properties: ClassVar[List[str]] = [ + "type", + "title", + "status", + "detail", + "instance", + ] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of Exception from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + * Fields in `self.additional_properties` are added to the output dict. + """ + _dict = self.model_dump( + by_alias=True, + exclude={ + "additional_properties", + }, + exclude_none=True, + ) + # puts key-value pairs in additional_properties in the top level + if self.additional_properties is not None: + for _key, _value in self.additional_properties.items(): + _dict[_key] = _value + + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of Exception from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "type": obj.get("type"), + "title": obj.get("title"), + "status": obj.get("status"), + "detail": obj.get("detail"), + "instance": obj.get("instance"), + } + ) + # store additional fields in additional_properties + for _key in obj.keys(): + if _key not in cls.__properties: + _obj.additional_properties[_key] = obj.get(_key) + + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/execute.py b/app-cp/src/unity_sps_ogc_processes_api/models/execute.py new file mode 100644 index 0000000..ce1da08 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/execute.py @@ -0,0 +1,133 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel + +from unity_sps_ogc_processes_api.models.input import Input +from unity_sps_ogc_processes_api.models.output import Output +from unity_sps_ogc_processes_api.models.subscriber import Subscriber + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Execute(BaseModel): + """ + Execute + """ # noqa: E501 + + inputs: Optional[Dict[str, Input]] = None + outputs: Optional[Dict[str, Output]] = None + subscriber: Optional[Subscriber] = None + __properties: ClassVar[List[str]] = ["inputs", "outputs", "subscriber"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of Execute from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each value in inputs (dict) + _field_dict = {} + if self.inputs: + for _key in self.inputs: + if self.inputs[_key]: + _field_dict[_key] = self.inputs[_key].to_dict() + _dict["inputs"] = _field_dict + # override the default output from pydantic by calling `to_dict()` of each value in outputs (dict) + _field_dict = {} + if self.outputs: + for _key in self.outputs: + if self.outputs[_key]: + _field_dict[_key] = self.outputs[_key].to_dict() + _dict["outputs"] = _field_dict + # override the default output from pydantic by calling `to_dict()` of subscriber + if self.subscriber: + _dict["subscriber"] = self.subscriber.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of Execute from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "inputs": ( + dict( + (_k, Input.from_dict(_v)) + for _k, _v in obj.get("inputs").items() + ) + if obj.get("inputs") is not None + else None + ), + "outputs": ( + dict( + (_k, Output.from_dict(_v)) + for _k, _v in obj.get("outputs").items() + ) + if obj.get("outputs") is not None + else None + ), + "subscriber": ( + Subscriber.from_dict(obj.get("subscriber")) + if obj.get("subscriber") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/execute200_response.py b/app-cp/src/unity_sps_ogc_processes_api/models/execute200_response.py new file mode 100644 index 0000000..ad9a1ce --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/execute200_response.py @@ -0,0 +1,310 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + FilePath, + StrictBool, + StrictBytes, + StrictFloat, + StrictInt, + StrictStr, + ValidationError, + field_validator, +) +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +EXECUTE200RESPONSE_ONE_OF_SCHEMAS = [ + "Dict[str, InlineOrRefData]", + "List[object]", + "bool", + "FilePath", + "float", + "int", + "object", + "str", +] + + +class Execute200Response(BaseModel): + """ + Execute200Response + """ + + # data type: str + oneof_schema_1_validator: Optional[StrictStr] = None + # data type: float + oneof_schema_2_validator: Optional[Union[StrictFloat, StrictInt]] = None + # data type: int + oneof_schema_3_validator: Optional[StrictInt] = None + # data type: object + oneof_schema_4_validator: Optional[Dict[str, Any]] = None + # data type: List[object] + oneof_schema_5_validator: Optional[List[Dict[str, Any]]] = None + # data type: bool + oneof_schema_6_validator: Optional[StrictBool] = None + # data type: FilePath + oneof_schema_7_validator: Optional[Union[StrictBytes, StrictStr]] = None + # data type: Dict[str, InlineOrRefData] + oneof_schema_8_validator: Optional[Dict[str, InlineOrRefData]] = None + actual_instance: Optional[ + Union[ + Dict[str, InlineOrRefData], + List[object], + bool, + FilePath, + float, + int, + object, + str, + ] + ] = None + one_of_schemas: List[str] = Literal[ + "Dict[str, InlineOrRefData]", + "List[object]", + "bool", + "FilePath", + "float", + "int", + "object", + "str", + ] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = Execute200Response.model_construct() + error_messages = [] + match = 0 + # validate data type: str + try: + instance.oneof_schema_1_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: float + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: int + try: + instance.oneof_schema_3_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: object + try: + instance.oneof_schema_4_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: List[object] + try: + instance.oneof_schema_5_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: bool + try: + instance.oneof_schema_6_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: FilePath + try: + instance.oneof_schema_7_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: Dict[str, InlineOrRefData] + try: + instance.oneof_schema_8_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in Execute200Response with oneOf schemas: Dict[str, InlineOrRefData], List[object], bool, FilePath, float, int, object, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in Execute200Response with oneOf schemas: Dict[str, InlineOrRefData], List[object], bool, FilePath, float, int, object, str. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into str + try: + # validation + instance.oneof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_1_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into float + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into int + try: + # validation + instance.oneof_schema_3_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_3_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into object + try: + # validation + instance.oneof_schema_4_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_4_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into List[object] + try: + # validation + instance.oneof_schema_5_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_5_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into bool + try: + # validation + instance.oneof_schema_6_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_6_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into FilePath + try: + # validation + instance.oneof_schema_7_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_7_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into Dict[str, InlineOrRefData] + try: + # validation + instance.oneof_schema_8_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_8_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into Execute200Response with oneOf schemas: Dict[str, InlineOrRefData], List[object], bool, FilePath, float, int, object, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into Execute200Response with oneOf schemas: Dict[str, InlineOrRefData], List[object], bool, FilePath, float, int, object, str. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/execute200_response1.py b/app-cp/src/unity_sps_ogc_processes_api/models/execute200_response1.py new file mode 100644 index 0000000..7392912 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/execute200_response1.py @@ -0,0 +1,124 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) +from typing_extensions import Annotated + +from unity_sps_ogc_processes_api.models.geo_json_feature import GeoJSONFeature + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Execute200Response1(BaseModel): + """ + Execute200Response1 + """ # noqa: E501 + + type: StrictStr + features: List[GeoJSONFeature] + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None + __properties: ClassVar[List[str]] = ["type", "features", "bbox"] + + @field_validator("type") + def type_validate_enum(cls, value): + """Validates the enum""" + if value not in ("FeatureCollection"): + raise ValueError("must be one of enum values ('FeatureCollection')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of Execute200Response1 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in features (list) + _items = [] + if self.features: + for _item in self.features: + if _item: + _items.append(_item.to_dict()) + _dict["features"] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of Execute200Response1 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "type": obj.get("type"), + "features": ( + [GeoJSONFeature.from_dict(_item) for _item in obj.get("features")] + if obj.get("features") is not None + else None + ), + "bbox": obj.get("bbox"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/execute_workflows.py b/app-cp/src/unity_sps_ogc_processes_api/models/execute_workflows.py new file mode 100644 index 0000000..fbe895c --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/execute_workflows.py @@ -0,0 +1,162 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( + FieldsModifiersProperties, +) +from unity_sps_ogc_processes_api.models.input_workflows import InputWorkflows +from unity_sps_ogc_processes_api.models.output_workflows import OutputWorkflows +from unity_sps_ogc_processes_api.models.subscriber import Subscriber + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class ExecuteWorkflows(BaseModel): + """ + ExecuteWorkflows + """ # noqa: E501 + + filter: Optional[StrictStr] = None + properties: Optional[FieldsModifiersProperties] = None + sort_by: Optional[List[StrictStr]] = Field(default=None, alias="sortBy") + process: Optional[StrictStr] = Field( + default=None, + description="URI to the process execution end point (i.e., `.../processes/{processId}/execution`)", + ) + inputs: Optional[Dict[str, InputWorkflows]] = None + outputs: Optional[Dict[str, OutputWorkflows]] = None + subscriber: Optional[Subscriber] = None + __properties: ClassVar[List[str]] = [ + "filter", + "properties", + "sortBy", + "process", + "inputs", + "outputs", + "subscriber", + ] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ExecuteWorkflows from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of properties + if self.properties: + _dict["properties"] = self.properties.to_dict() + # override the default output from pydantic by calling `to_dict()` of each value in inputs (dict) + _field_dict = {} + if self.inputs: + for _key in self.inputs: + if self.inputs[_key]: + _field_dict[_key] = self.inputs[_key].to_dict() + _dict["inputs"] = _field_dict + # override the default output from pydantic by calling `to_dict()` of each value in outputs (dict) + _field_dict = {} + if self.outputs: + for _key in self.outputs: + if self.outputs[_key]: + _field_dict[_key] = self.outputs[_key].to_dict() + _dict["outputs"] = _field_dict + # override the default output from pydantic by calling `to_dict()` of subscriber + if self.subscriber: + _dict["subscriber"] = self.subscriber.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of ExecuteWorkflows from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "filter": obj.get("filter"), + "properties": ( + FieldsModifiersProperties.from_dict(obj.get("properties")) + if obj.get("properties") is not None + else None + ), + "sortBy": obj.get("sortBy"), + "process": obj.get("process"), + "inputs": ( + dict( + (_k, InputWorkflows.from_dict(_v)) + for _k, _v in obj.get("inputs").items() + ) + if obj.get("inputs") is not None + else None + ), + "outputs": ( + dict( + (_k, OutputWorkflows.from_dict(_v)) + for _k, _v in obj.get("outputs").items() + ) + if obj.get("outputs") is not None + else None + ), + "subscriber": ( + Subscriber.from_dict(obj.get("subscriber")) + if obj.get("subscriber") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/execute_workflows1.py b/app-cp/src/unity_sps_ogc_processes_api/models/execute_workflows1.py new file mode 100644 index 0000000..0336057 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/execute_workflows1.py @@ -0,0 +1,162 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( + FieldsModifiersProperties, +) +from unity_sps_ogc_processes_api.models.input_workflows1 import InputWorkflows1 +from unity_sps_ogc_processes_api.models.output_workflows1 import OutputWorkflows1 +from unity_sps_ogc_processes_api.models.subscriber import Subscriber + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class ExecuteWorkflows1(BaseModel): + """ + ExecuteWorkflows1 + """ # noqa: E501 + + filter: Optional[StrictStr] = None + properties: Optional[FieldsModifiersProperties] = None + sort_by: Optional[List[StrictStr]] = Field(default=None, alias="sortBy") + process: Optional[StrictStr] = Field( + default=None, + description="URI to the process execution end point (i.e., `.../processes/{processId}/execution`)", + ) + inputs: Optional[Dict[str, InputWorkflows1]] = None + outputs: Optional[Dict[str, OutputWorkflows1]] = None + subscriber: Optional[Subscriber] = None + __properties: ClassVar[List[str]] = [ + "filter", + "properties", + "sortBy", + "process", + "inputs", + "outputs", + "subscriber", + ] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ExecuteWorkflows1 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of properties + if self.properties: + _dict["properties"] = self.properties.to_dict() + # override the default output from pydantic by calling `to_dict()` of each value in inputs (dict) + _field_dict = {} + if self.inputs: + for _key in self.inputs: + if self.inputs[_key]: + _field_dict[_key] = self.inputs[_key].to_dict() + _dict["inputs"] = _field_dict + # override the default output from pydantic by calling `to_dict()` of each value in outputs (dict) + _field_dict = {} + if self.outputs: + for _key in self.outputs: + if self.outputs[_key]: + _field_dict[_key] = self.outputs[_key].to_dict() + _dict["outputs"] = _field_dict + # override the default output from pydantic by calling `to_dict()` of subscriber + if self.subscriber: + _dict["subscriber"] = self.subscriber.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of ExecuteWorkflows1 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "filter": obj.get("filter"), + "properties": ( + FieldsModifiersProperties.from_dict(obj.get("properties")) + if obj.get("properties") is not None + else None + ), + "sortBy": obj.get("sortBy"), + "process": obj.get("process"), + "inputs": ( + dict( + (_k, InputWorkflows1.from_dict(_v)) + for _k, _v in obj.get("inputs").items() + ) + if obj.get("inputs") is not None + else None + ), + "outputs": ( + dict( + (_k, OutputWorkflows1.from_dict(_v)) + for _k, _v in obj.get("outputs").items() + ) + if obj.get("outputs") is not None + else None + ), + "subscriber": ( + Subscriber.from_dict(obj.get("subscriber")) + if obj.get("subscriber") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/execution_unit.py b/app-cp/src/unity_sps_ogc_processes_api/models/execution_unit.py new file mode 100644 index 0000000..db04341 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/execution_unit.py @@ -0,0 +1,142 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr, field_validator + +from unity_sps_ogc_processes_api.models.execution_unit_config import ExecutionUnitConfig + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class ExecutionUnit(BaseModel): + """ + Resource containing an executable or runtime information for executing the process. + """ # noqa: E501 + + type: StrictStr = Field(description="Type of execution unit.") + image: StrictStr = Field( + description="Container image reference for the execution unit." + ) + deployment: Optional[StrictStr] = Field( + default=None, description="Deployment information for the execution unit." + ) + config: Optional[ExecutionUnitConfig] = None + additional_properties: Dict[str, Any] = {} + __properties: ClassVar[List[str]] = ["type", "image", "deployment", "config"] + + @field_validator("type") + def type_validate_enum(cls, value): + """Validates the enum""" + if value not in ("docker", "oci"): + raise ValueError("must be one of enum values ('docker', 'oci')") + return value + + @field_validator("deployment") + def deployment_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in ("local", "remote", "hpc", "cloud"): + raise ValueError( + "must be one of enum values ('local', 'remote', 'hpc', 'cloud')" + ) + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ExecutionUnit from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + * Fields in `self.additional_properties` are added to the output dict. + """ + _dict = self.model_dump( + by_alias=True, + exclude={ + "additional_properties", + }, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of config + if self.config: + _dict["config"] = self.config.to_dict() + # puts key-value pairs in additional_properties in the top level + if self.additional_properties is not None: + for _key, _value in self.additional_properties.items(): + _dict[_key] = _value + + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of ExecutionUnit from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "type": obj.get("type"), + "image": obj.get("image"), + "deployment": obj.get("deployment"), + "config": ( + ExecutionUnitConfig.from_dict(obj.get("config")) + if obj.get("config") is not None + else None + ), + } + ) + # store additional fields in additional_properties + for _key in obj.keys(): + if _key not in cls.__properties: + _obj.additional_properties[_key] = obj.get(_key) + + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/execution_unit_config.py b/app-cp/src/unity_sps_ogc_processes_api/models/execution_unit_config.py new file mode 100644 index 0000000..8ef1b2e --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/execution_unit_config.py @@ -0,0 +1,158 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import BaseModel, Field, StrictFloat, StrictInt +from typing_extensions import Annotated + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class ExecutionUnitConfig(BaseModel): + """ + Hardware requirements and configuration properties for executing the process. + """ # noqa: E501 + + cpu_min: Optional[ + Union[ + Annotated[float, Field(strict=True, ge=1)], + Annotated[int, Field(strict=True, ge=1)], + ] + ] = Field( + default=None, + description="Minimum number of CPUs required to run the process (unit is CPU core).", + alias="cpuMin", + ) + cpu_max: Optional[Union[StrictFloat, StrictInt]] = Field( + default=None, + description="Maximum number of CPU dedicated to the process (unit is CPU core)", + alias="cpuMax", + ) + memory_min: Optional[Union[StrictFloat, StrictInt]] = Field( + default=None, + description="Minimum RAM memory required to run the application (unit is GB)", + alias="memoryMin", + ) + memory_max: Optional[Union[StrictFloat, StrictInt]] = Field( + default=None, + description="Maximum RAM memory dedicated to the application (unit is GB)", + alias="memoryMax", + ) + storage_temp_min: Optional[Union[StrictFloat, StrictInt]] = Field( + default=None, + description="Minimum required temporary storage size (unit is GB)", + alias="storageTempMin", + ) + storage_outputs_min: Optional[Union[StrictFloat, StrictInt]] = Field( + default=None, + description="Minimum required output storage size (unit is GB)", + alias="storageOutputsMin", + ) + job_timeout: Optional[Union[StrictFloat, StrictInt]] = Field( + default=None, + description="Timeout delay for a job execution (in seconds)", + alias="jobTimeout", + ) + additional_properties: Dict[str, Any] = {} + __properties: ClassVar[List[str]] = [ + "cpuMin", + "cpuMax", + "memoryMin", + "memoryMax", + "storageTempMin", + "storageOutputsMin", + "jobTimeout", + ] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ExecutionUnitConfig from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + * Fields in `self.additional_properties` are added to the output dict. + """ + _dict = self.model_dump( + by_alias=True, + exclude={ + "additional_properties", + }, + exclude_none=True, + ) + # puts key-value pairs in additional_properties in the top level + if self.additional_properties is not None: + for _key, _value in self.additional_properties.items(): + _dict[_key] = _value + + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of ExecutionUnitConfig from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "cpuMin": obj.get("cpuMin"), + "cpuMax": obj.get("cpuMax"), + "memoryMin": obj.get("memoryMin"), + "memoryMax": obj.get("memoryMax"), + "storageTempMin": obj.get("storageTempMin"), + "storageOutputsMin": obj.get("storageOutputsMin"), + "jobTimeout": obj.get("jobTimeout"), + } + ) + # store additional fields in additional_properties + for _key in obj.keys(): + if _key not in cls.__properties: + _obj.additional_properties[_key] = obj.get(_key) + + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent.py b/app-cp/src/unity_sps_ogc_processes_api/models/extent.py new file mode 100644 index 0000000..f5f48ee --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/extent.py @@ -0,0 +1,109 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel + +from unity_sps_ogc_processes_api.models.extent_spatial import ExtentSpatial +from unity_sps_ogc_processes_api.models.extent_temporal import ExtentTemporal + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Extent(BaseModel): + """ + The extent of the data in the collection. In the Core only spatial and temporal extents are specified. Extensions may add additional members to represent other extents, for example, thermal or pressure ranges. The first item in the array describes the overall extent of the data. All subsequent items describe more precise extents, e.g., to identify clusters of data. Clients only interested in the overall extent will only need to access the first item in each array. + """ # noqa: E501 + + spatial: Optional[ExtentSpatial] = None + temporal: Optional[ExtentTemporal] = None + __properties: ClassVar[List[str]] = ["spatial", "temporal"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of Extent from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of spatial + if self.spatial: + _dict["spatial"] = self.spatial.to_dict() + # override the default output from pydantic by calling `to_dict()` of temporal + if self.temporal: + _dict["temporal"] = self.temporal.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of Extent from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "spatial": ( + ExtentSpatial.from_dict(obj.get("spatial")) + if obj.get("spatial") is not None + else None + ), + "temporal": ( + ExtentTemporal.from_dict(obj.get("temporal")) + if obj.get("temporal") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial.py b/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial.py new file mode 100644 index 0000000..15666e6 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial.py @@ -0,0 +1,148 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) +from typing_extensions import Annotated + +from unity_sps_ogc_processes_api.models.extent_spatial_grid_inner import ( + ExtentSpatialGridInner, +) + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class ExtentSpatial(BaseModel): + """ + The spatial extent of the data in the collection. + """ # noqa: E501 + + bbox: Optional[ + Annotated[List[List[Union[StrictFloat, StrictInt]]], Field(min_length=1)] + ] = Field( + default=None, + description="One or more bounding boxes that describe the spatial extent of the dataset. In the Core only a single bounding box is supported. Extensions may support additional areas. The first bounding box describes the overall spatial extent of the data. All subsequent bounding boxes describe more precise bounding boxes, e.g., to identify clusters of data. Clients only interested in the overall spatial extent will only need to access the first item in each array.", + ) + crs: Optional[StrictStr] = Field( + default="1.3/CRS84", + description="Coordinate reference system of the coordinates in the spatial extent (property `bbox`). The default reference system is WGS 84 longitude/latitude. In the Core the only other supported coordinate reference system is WGS 84 longitude/latitude/ellipsoidal height for coordinates with height. Extensions may support additional coordinate reference systems and add additional enum values.", + ) + grid: Optional[ + Annotated[List[ExtentSpatialGridInner], Field(min_length=2, max_length=3)] + ] = Field( + default=None, + description="Provides information about the limited availability of data within the collection organized as a grid (regular or irregular) along each spatial dimension.", + ) + __properties: ClassVar[List[str]] = ["bbox", "crs", "grid"] + + @field_validator("crs") + def crs_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in ( + "http://www.opengis.net/def/crs/OGC/1.3/CRS84", + "http://www.opengis.net/def/crs/OGC/0/CRS84h", + ): + raise ValueError( + "must be one of enum values ('http://www.opengis.net/def/crs/OGC/1.3/CRS84', 'http://www.opengis.net/def/crs/OGC/0/CRS84h')" + ) + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ExtentSpatial from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in grid (list) + _items = [] + if self.grid: + for _item in self.grid: + if _item: + _items.append(_item.to_dict()) + _dict["grid"] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of ExtentSpatial from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "bbox": obj.get("bbox"), + "crs": obj.get("crs") if obj.get("crs") is not None else "1.3/CRS84", + "grid": ( + [ + ExtentSpatialGridInner.from_dict(_item) + for _item in obj.get("grid") + ] + if obj.get("grid") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py b/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py new file mode 100644 index 0000000..d943b10 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py @@ -0,0 +1,132 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictInt +from typing_extensions import Annotated + +from unity_sps_ogc_processes_api.models.extent_spatial_grid_inner_coordinates_inner import ( + ExtentSpatialGridInnerCoordinatesInner, +) +from unity_sps_ogc_processes_api.models.extent_spatial_grid_inner_resolution import ( + ExtentSpatialGridInnerResolution, +) + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class ExtentSpatialGridInner(BaseModel): + """ + ExtentSpatialGridInner + """ # noqa: E501 + + coordinates: Optional[ + Annotated[List[ExtentSpatialGridInnerCoordinatesInner], Field(min_length=1)] + ] = Field( + default=None, + description="List of coordinates along the dimension for which data organized as an irregular grid in the collection is available (e.g., 2, 10, 80, 100).", + ) + cells_count: Optional[StrictInt] = Field( + default=None, + description="Number of samples available along the dimension for data organized as a regular grid. For values representing the whole area of contiguous cells spanning _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_. For values representing infinitely small point cells spaced by _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_ + 1.", + alias="cellsCount", + ) + resolution: Optional[ExtentSpatialGridInnerResolution] = None + __properties: ClassVar[List[str]] = ["coordinates", "cellsCount", "resolution"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ExtentSpatialGridInner from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in coordinates (list) + _items = [] + if self.coordinates: + for _item in self.coordinates: + if _item: + _items.append(_item.to_dict()) + _dict["coordinates"] = _items + # override the default output from pydantic by calling `to_dict()` of resolution + if self.resolution: + _dict["resolution"] = self.resolution.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of ExtentSpatialGridInner from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "coordinates": ( + [ + ExtentSpatialGridInnerCoordinatesInner.from_dict(_item) + for _item in obj.get("coordinates") + ] + if obj.get("coordinates") is not None + else None + ), + "cellsCount": obj.get("cellsCount"), + "resolution": ( + ExtentSpatialGridInnerResolution.from_dict(obj.get("resolution")) + if obj.get("resolution") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py b/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py new file mode 100644 index 0000000..40e0685 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py @@ -0,0 +1,174 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + StrictFloat, + StrictInt, + StrictStr, + ValidationError, + field_validator, +) +from typing_extensions import Literal + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +EXTENTSPATIALGRIDINNERCOORDINATESINNER_ONE_OF_SCHEMAS = ["float", "str"] + + +class ExtentSpatialGridInnerCoordinatesInner(BaseModel): + """ + ExtentSpatialGridInnerCoordinatesInner + """ + + # data type: str + oneof_schema_1_validator: Optional[StrictStr] = None + # data type: float + oneof_schema_2_validator: Optional[Union[StrictFloat, StrictInt]] = None + actual_instance: Optional[Union[float, str]] = None + one_of_schemas: List[str] = Literal["float", "str"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = ExtentSpatialGridInnerCoordinatesInner.model_construct() + error_messages = [] + match = 0 + # validate data type: str + try: + instance.oneof_schema_1_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: float + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in ExtentSpatialGridInnerCoordinatesInner with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in ExtentSpatialGridInnerCoordinatesInner with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into str + try: + # validation + instance.oneof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_1_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into float + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into ExtentSpatialGridInnerCoordinatesInner with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into ExtentSpatialGridInnerCoordinatesInner with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py b/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py new file mode 100644 index 0000000..4f87b6c --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py @@ -0,0 +1,174 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + StrictFloat, + StrictInt, + StrictStr, + ValidationError, + field_validator, +) +from typing_extensions import Literal + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +EXTENTSPATIALGRIDINNERRESOLUTION_ONE_OF_SCHEMAS = ["float", "str"] + + +class ExtentSpatialGridInnerResolution(BaseModel): + """ + Resolution of regularly gridded data along the dimension in the collection + """ + + # data type: str + oneof_schema_1_validator: Optional[StrictStr] = None + # data type: float + oneof_schema_2_validator: Optional[Union[StrictFloat, StrictInt]] = None + actual_instance: Optional[Union[float, str]] = None + one_of_schemas: List[str] = Literal["float", "str"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = ExtentSpatialGridInnerResolution.model_construct() + error_messages = [] + match = 0 + # validate data type: str + try: + instance.oneof_schema_1_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: float + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in ExtentSpatialGridInnerResolution with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in ExtentSpatialGridInnerResolution with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into str + try: + # validation + instance.oneof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_1_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into float + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into ExtentSpatialGridInnerResolution with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into ExtentSpatialGridInnerResolution with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal.py b/app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal.py new file mode 100644 index 0000000..a30bbec --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal.py @@ -0,0 +1,134 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from datetime import datetime +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr, field_validator +from typing_extensions import Annotated + +from unity_sps_ogc_processes_api.models.extent_temporal_grid import ExtentTemporalGrid + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class ExtentTemporal(BaseModel): + """ + The temporal extent of the features in the collection. + """ # noqa: E501 + + interval: Optional[ + Annotated[ + List[ + Annotated[List[Optional[datetime]], Field(min_length=2, max_length=2)] + ], + Field(min_length=1), + ] + ] = Field( + default=None, + description="One or more time intervals that describe the temporal extent of the dataset. In the Core only a single time interval is supported. Extensions may support multiple intervals. The first time interval describes the overall temporal extent of the data. All subsequent time intervals describe more precise time intervals, e.g., to identify clusters of data. Clients only interested in the overall extent will only need to access the first item in each array.", + ) + trs: Optional[StrictStr] = Field( + default="http://www.opengis.net/def/uom/ISO-8601/0/Gregorian", + description="Coordinate reference system of the coordinates in the temporal extent (property `interval`). The default reference system is the Gregorian calendar. In the Core this is the only supported temporal coordinate reference system. Extensions may support additional temporal coordinate reference systems and add additional enum values.", + ) + grid: Optional[ExtentTemporalGrid] = None + __properties: ClassVar[List[str]] = ["interval", "trs", "grid"] + + @field_validator("trs") + def trs_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in ("http://www.opengis.net/def/uom/ISO-8601/0/Gregorian"): + raise ValueError( + "must be one of enum values ('http://www.opengis.net/def/uom/ISO-8601/0/Gregorian')" + ) + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ExtentTemporal from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of grid + if self.grid: + _dict["grid"] = self.grid.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of ExtentTemporal from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "interval": obj.get("interval"), + "trs": ( + obj.get("trs") + if obj.get("trs") is not None + else "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian" + ), + "grid": ( + ExtentTemporalGrid.from_dict(obj.get("grid")) + if obj.get("grid") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py b/app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py new file mode 100644 index 0000000..8b24a5b --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py @@ -0,0 +1,115 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictInt, StrictStr +from typing_extensions import Annotated + +from unity_sps_ogc_processes_api.models.extent_temporal_grid_resolution import ( + ExtentTemporalGridResolution, +) + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class ExtentTemporalGrid(BaseModel): + """ + Provides information about the limited availability of data within the collection organized as a grid (regular or irregular) along the temporal dimension. + """ # noqa: E501 + + coordinates: Optional[Annotated[List[Optional[StrictStr]], Field(min_length=1)]] = ( + Field( + default=None, + description='List of coordinates along the temporal dimension for which data organized as an irregular grid in the collection is available (e.g., "2017-11-14T09:00Z","2017-11-14T12:00Z","2017-11-14T15:00Z","2017-11-14T18:00Z","2017-11-14T21:00Z").', + ) + ) + cells_count: Optional[StrictInt] = Field( + default=None, + description="Number of samples available along the temporal dimension for data organized as a regular grid. For values representing the whole area of contiguous cells spanning _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_. For values representing infinitely small point cells spaced by _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_ + 1.", + alias="cellsCount", + ) + resolution: Optional[ExtentTemporalGridResolution] = None + __properties: ClassVar[List[str]] = ["coordinates", "cellsCount", "resolution"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ExtentTemporalGrid from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of resolution + if self.resolution: + _dict["resolution"] = self.resolution.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of ExtentTemporalGrid from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "coordinates": obj.get("coordinates"), + "cellsCount": obj.get("cellsCount"), + "resolution": ( + ExtentTemporalGridResolution.from_dict(obj.get("resolution")) + if obj.get("resolution") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py b/app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py new file mode 100644 index 0000000..6164e15 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py @@ -0,0 +1,174 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + StrictFloat, + StrictInt, + StrictStr, + ValidationError, + field_validator, +) +from typing_extensions import Literal + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +EXTENTTEMPORALGRIDRESOLUTION_ONE_OF_SCHEMAS = ["float", "str"] + + +class ExtentTemporalGridResolution(BaseModel): + """ + Resolution of regularly gridded data along the temporal dimension in the collection + """ + + # data type: str + oneof_schema_1_validator: Optional[StrictStr] = None + # data type: float + oneof_schema_2_validator: Optional[Union[StrictFloat, StrictInt]] = None + actual_instance: Optional[Union[float, str]] = None + one_of_schemas: List[str] = Literal["float", "str"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = ExtentTemporalGridResolution.model_construct() + error_messages = [] + match = 0 + # validate data type: str + try: + instance.oneof_schema_1_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: float + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in ExtentTemporalGridResolution with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in ExtentTemporalGridResolution with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into str + try: + # validation + instance.oneof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_1_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into float + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into ExtentTemporalGridResolution with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into ExtentTemporalGridResolution with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_uad.py b/app-cp/src/unity_sps_ogc_processes_api/models/extent_uad.py new file mode 100644 index 0000000..6a09966 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/extent_uad.py @@ -0,0 +1,109 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel + +from unity_sps_ogc_processes_api.models.extent_spatial import ExtentSpatial +from unity_sps_ogc_processes_api.models.extent_temporal import ExtentTemporal + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class ExtentUad(BaseModel): + """ + The extent module only addresses spatial and temporal extents. This module extends extent by specifying how intervals and crs properties can be used to specify additional geometries. + """ # noqa: E501 + + spatial: Optional[ExtentSpatial] = None + temporal: Optional[ExtentTemporal] = None + __properties: ClassVar[List[str]] = ["spatial", "temporal"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ExtentUad from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of spatial + if self.spatial: + _dict["spatial"] = self.spatial.to_dict() + # override the default output from pydantic by calling `to_dict()` of temporal + if self.temporal: + _dict["temporal"] = self.temporal.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of ExtentUad from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "spatial": ( + ExtentSpatial.from_dict(obj.get("spatial")) + if obj.get("spatial") is not None + else None + ), + "temporal": ( + ExtentTemporal.from_dict(obj.get("temporal")) + if obj.get("temporal") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extra_models.py b/app-cp/src/unity_sps_ogc_processes_api/models/extra_models.py new file mode 100644 index 0000000..f0588d2 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/extra_models.py @@ -0,0 +1,9 @@ +# coding: utf-8 + +from pydantic import BaseModel + + +class TokenModel(BaseModel): + """Defines a token model.""" + + sub: str diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/feature_collection.py b/app-cp/src/unity_sps_ogc_processes_api/models/feature_collection.py new file mode 100644 index 0000000..d9d2f08 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/feature_collection.py @@ -0,0 +1,124 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) +from typing_extensions import Annotated + +from unity_sps_ogc_processes_api.models.geo_json_feature import GeoJSONFeature + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class FeatureCollection(BaseModel): + """ + FeatureCollection + """ # noqa: E501 + + type: StrictStr + features: List[GeoJSONFeature] + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None + __properties: ClassVar[List[str]] = ["type", "features", "bbox"] + + @field_validator("type") + def type_validate_enum(cls, value): + """Validates the enum""" + if value not in ("FeatureCollection"): + raise ValueError("must be one of enum values ('FeatureCollection')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of FeatureCollection from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in features (list) + _items = [] + if self.features: + for _item in self.features: + if _item: + _items.append(_item.to_dict()) + _dict["features"] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of FeatureCollection from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "type": obj.get("type"), + "features": ( + [GeoJSONFeature.from_dict(_item) for _item in obj.get("features")] + if obj.get("features") is not None + else None + ), + "bbox": obj.get("bbox"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers.py b/app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers.py new file mode 100644 index 0000000..9b71705 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers.py @@ -0,0 +1,105 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( + FieldsModifiersProperties, +) + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class FieldsModifiers(BaseModel): + """ + FieldsModifiers + """ # noqa: E501 + + filter: Optional[StrictStr] = None + properties: Optional[FieldsModifiersProperties] = None + sort_by: Optional[List[StrictStr]] = Field(default=None, alias="sortBy") + __properties: ClassVar[List[str]] = ["filter", "properties", "sortBy"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of FieldsModifiers from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of properties + if self.properties: + _dict["properties"] = self.properties.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of FieldsModifiers from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "filter": obj.get("filter"), + "properties": ( + FieldsModifiersProperties.from_dict(obj.get("properties")) + if obj.get("properties") is not None + else None + ), + "sortBy": obj.get("sortBy"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py b/app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py new file mode 100644 index 0000000..e5c7919 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py @@ -0,0 +1,167 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, StrictStr, ValidationError, field_validator +from typing_extensions import Literal + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +FIELDSMODIFIERSPROPERTIES_ONE_OF_SCHEMAS = ["Dict[str, str]", "List[str]"] + + +class FieldsModifiersProperties(BaseModel): + """ + FieldsModifiersProperties + """ + + # data type: Dict[str, str] + oneof_schema_1_validator: Optional[Dict[str, StrictStr]] = None + # data type: List[str] + oneof_schema_2_validator: Optional[List[StrictStr]] = None + actual_instance: Optional[Union[Dict[str, str], List[str]]] = None + one_of_schemas: List[str] = Literal["Dict[str, str]", "List[str]"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = FieldsModifiersProperties.model_construct() + error_messages = [] + match = 0 + # validate data type: Dict[str, str] + try: + instance.oneof_schema_1_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: List[str] + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in FieldsModifiersProperties with oneOf schemas: Dict[str, str], List[str]. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in FieldsModifiersProperties with oneOf schemas: Dict[str, str], List[str]. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into Dict[str, str] + try: + # validation + instance.oneof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_1_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into List[str] + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into FieldsModifiersProperties with oneOf schemas: Dict[str, str], List[str]. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into FieldsModifiersProperties with oneOf schemas: Dict[str, str], List[str]. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/format.py b/app-cp/src/unity_sps_ogc_processes_api/models/format.py new file mode 100644 index 0000000..eea085c --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/format.py @@ -0,0 +1,103 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.format_schema import FormatSchema + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Format(BaseModel): + """ + Format + """ # noqa: E501 + + media_type: Optional[StrictStr] = Field(default=None, alias="mediaType") + encoding: Optional[StrictStr] = None + schema: Optional[FormatSchema] = Field(default=None, alias="schema") + __properties: ClassVar[List[str]] = ["mediaType", "encoding", "schema"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of Format from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of schema + if self.schema: + _dict["schema"] = self.schema.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of Format from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "mediaType": obj.get("mediaType"), + "encoding": obj.get("encoding"), + "schema": ( + FormatSchema.from_dict(obj.get("schema")) + if obj.get("schema") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/format_schema.py b/app-cp/src/unity_sps_ogc_processes_api/models/format_schema.py new file mode 100644 index 0000000..f0f0067 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/format_schema.py @@ -0,0 +1,167 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, Dict, List, Optional, Union + +from pydantic import BaseModel, StrictStr, ValidationError, field_validator +from typing_extensions import Literal + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +FORMATSCHEMA_ONE_OF_SCHEMAS = ["object", "str"] + + +class FormatSchema(BaseModel): + """ + FormatSchema + """ + + # data type: str + oneof_schema_1_validator: Optional[StrictStr] = None + # data type: object + oneof_schema_2_validator: Optional[Dict[str, Any]] = None + actual_instance: Optional[Union[object, str]] = None + one_of_schemas: List[str] = Literal["object", "str"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = FormatSchema.model_construct() + error_messages = [] + match = 0 + # validate data type: str + try: + instance.oneof_schema_1_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: object + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in FormatSchema with oneOf schemas: object, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in FormatSchema with oneOf schemas: object, str. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into str + try: + # validation + instance.oneof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_1_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into object + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into FormatSchema with oneOf schemas: object, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into FormatSchema with oneOf schemas: object, str. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature.py b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature.py new file mode 100644 index 0000000..ce8da2e --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature.py @@ -0,0 +1,139 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) +from typing_extensions import Annotated + +from unity_sps_ogc_processes_api.models.geo_json_feature_geometry import ( + GeoJSONFeatureGeometry, +) +from unity_sps_ogc_processes_api.models.geo_json_feature_id import GeoJSONFeatureId + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class GeoJSONFeature(BaseModel): + """ + GeoJSONFeature + """ # noqa: E501 + + type: StrictStr + id: Optional[GeoJSONFeatureId] = None + properties: Optional[Dict[str, Any]] + geometry: GeoJSONFeatureGeometry + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None + __properties: ClassVar[List[str]] = ["type", "id", "properties", "geometry", "bbox"] + + @field_validator("type") + def type_validate_enum(cls, value): + """Validates the enum""" + if value not in ("Feature"): + raise ValueError("must be one of enum values ('Feature')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of GeoJSONFeature from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of id + if self.id: + _dict["id"] = self.id.to_dict() + # override the default output from pydantic by calling `to_dict()` of geometry + if self.geometry: + _dict["geometry"] = self.geometry.to_dict() + # set to None if properties (nullable) is None + # and model_fields_set contains the field + if self.properties is None and "properties" in self.model_fields_set: + _dict["properties"] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of GeoJSONFeature from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "type": obj.get("type"), + "id": ( + GeoJSONFeatureId.from_dict(obj.get("id")) + if obj.get("id") is not None + else None + ), + "properties": obj.get("properties"), + "geometry": ( + GeoJSONFeatureGeometry.from_dict(obj.get("geometry")) + if obj.get("geometry") is not None + else None + ), + "bbox": obj.get("bbox"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py new file mode 100644 index 0000000..557cefd --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py @@ -0,0 +1,257 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.geo_json_line_string import GeoJSONLineString +from unity_sps_ogc_processes_api.models.geo_json_multi_line_string import ( + GeoJSONMultiLineString, +) +from unity_sps_ogc_processes_api.models.geo_json_multi_point import GeoJSONMultiPoint +from unity_sps_ogc_processes_api.models.geo_json_multi_polygon import ( + GeoJSONMultiPolygon, +) +from unity_sps_ogc_processes_api.models.geo_json_point import GeoJSONPoint +from unity_sps_ogc_processes_api.models.geo_json_polygon import GeoJSONPolygon + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +GEOJSONFEATUREGEOMETRY_ONE_OF_SCHEMAS = [ + "GeoJSONLineString", + "GeoJSONMultiLineString", + "GeoJSONMultiPoint", + "GeoJSONMultiPolygon", + "GeoJSONPoint", + "GeoJSONPolygon", +] + + +class GeoJSONFeatureGeometry(BaseModel): + """ + GeoJSONFeatureGeometry + """ + + # data type: GeoJSONPoint + oneof_schema_1_validator: Optional[GeoJSONPoint] = None + # data type: GeoJSONLineString + oneof_schema_2_validator: Optional[GeoJSONLineString] = None + # data type: GeoJSONPolygon + oneof_schema_3_validator: Optional[GeoJSONPolygon] = None + # data type: GeoJSONMultiPoint + oneof_schema_4_validator: Optional[GeoJSONMultiPoint] = None + # data type: GeoJSONMultiLineString + oneof_schema_5_validator: Optional[GeoJSONMultiLineString] = None + # data type: GeoJSONMultiPolygon + oneof_schema_6_validator: Optional[GeoJSONMultiPolygon] = None + actual_instance: Optional[ + Union[ + GeoJSONLineString, + GeoJSONMultiLineString, + GeoJSONMultiPoint, + GeoJSONMultiPolygon, + GeoJSONPoint, + GeoJSONPolygon, + ] + ] = None + one_of_schemas: List[str] = Literal[ + "GeoJSONLineString", + "GeoJSONMultiLineString", + "GeoJSONMultiPoint", + "GeoJSONMultiPolygon", + "GeoJSONPoint", + "GeoJSONPolygon", + ] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + GeoJSONFeatureGeometry.model_construct() + error_messages = [] + match = 0 + # validate data type: GeoJSONPoint + if not isinstance(v, GeoJSONPoint): + error_messages.append( + f"Error! Input type `{type(v)}` is not `GeoJSONPoint`" + ) + else: + match += 1 + # validate data type: GeoJSONLineString + if not isinstance(v, GeoJSONLineString): + error_messages.append( + f"Error! Input type `{type(v)}` is not `GeoJSONLineString`" + ) + else: + match += 1 + # validate data type: GeoJSONPolygon + if not isinstance(v, GeoJSONPolygon): + error_messages.append( + f"Error! Input type `{type(v)}` is not `GeoJSONPolygon`" + ) + else: + match += 1 + # validate data type: GeoJSONMultiPoint + if not isinstance(v, GeoJSONMultiPoint): + error_messages.append( + f"Error! Input type `{type(v)}` is not `GeoJSONMultiPoint`" + ) + else: + match += 1 + # validate data type: GeoJSONMultiLineString + if not isinstance(v, GeoJSONMultiLineString): + error_messages.append( + f"Error! Input type `{type(v)}` is not `GeoJSONMultiLineString`" + ) + else: + match += 1 + # validate data type: GeoJSONMultiPolygon + if not isinstance(v, GeoJSONMultiPolygon): + error_messages.append( + f"Error! Input type `{type(v)}` is not `GeoJSONMultiPolygon`" + ) + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in GeoJSONFeatureGeometry with oneOf schemas: GeoJSONLineString, GeoJSONMultiLineString, GeoJSONMultiPoint, GeoJSONMultiPolygon, GeoJSONPoint, GeoJSONPolygon. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in GeoJSONFeatureGeometry with oneOf schemas: GeoJSONLineString, GeoJSONMultiLineString, GeoJSONMultiPoint, GeoJSONMultiPolygon, GeoJSONPoint, GeoJSONPolygon. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into GeoJSONPoint + try: + instance.actual_instance = GeoJSONPoint.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into GeoJSONLineString + try: + instance.actual_instance = GeoJSONLineString.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into GeoJSONPolygon + try: + instance.actual_instance = GeoJSONPolygon.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into GeoJSONMultiPoint + try: + instance.actual_instance = GeoJSONMultiPoint.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into GeoJSONMultiLineString + try: + instance.actual_instance = GeoJSONMultiLineString.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into GeoJSONMultiPolygon + try: + instance.actual_instance = GeoJSONMultiPolygon.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into GeoJSONFeatureGeometry with oneOf schemas: GeoJSONLineString, GeoJSONMultiLineString, GeoJSONMultiPoint, GeoJSONMultiPolygon, GeoJSONPoint, GeoJSONPolygon. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into GeoJSONFeatureGeometry with oneOf schemas: GeoJSONLineString, GeoJSONMultiLineString, GeoJSONMultiPoint, GeoJSONMultiPolygon, GeoJSONPoint, GeoJSONPolygon. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py new file mode 100644 index 0000000..7062157 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py @@ -0,0 +1,174 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + StrictFloat, + StrictInt, + StrictStr, + ValidationError, + field_validator, +) +from typing_extensions import Literal + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +GEOJSONFEATUREID_ONE_OF_SCHEMAS = ["float", "str"] + + +class GeoJSONFeatureId(BaseModel): + """ + GeoJSONFeatureId + """ + + # data type: float + oneof_schema_1_validator: Optional[Union[StrictFloat, StrictInt]] = None + # data type: str + oneof_schema_2_validator: Optional[StrictStr] = None + actual_instance: Optional[Union[float, str]] = None + one_of_schemas: List[str] = Literal["float", "str"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = GeoJSONFeatureId.model_construct() + error_messages = [] + match = 0 + # validate data type: float + try: + instance.oneof_schema_1_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: str + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in GeoJSONFeatureId with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in GeoJSONFeatureId with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into float + try: + # validation + instance.oneof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_1_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into str + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into GeoJSONFeatureId with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into GeoJSONFeatureId with oneOf schemas: float, str. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py new file mode 100644 index 0000000..5e38591 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py @@ -0,0 +1,114 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) +from typing_extensions import Annotated + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class GeoJSONLineString(BaseModel): + """ + GeoJSONLineString + """ # noqa: E501 + + type: StrictStr + coordinates: Annotated[ + List[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)]], + Field(min_length=2), + ] + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None + __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] + + @field_validator("type") + def type_validate_enum(cls, value): + """Validates the enum""" + if value not in ("LineString"): + raise ValueError("must be one of enum values ('LineString')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of GeoJSONLineString from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of GeoJSONLineString from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "type": obj.get("type"), + "coordinates": obj.get("coordinates"), + "bbox": obj.get("bbox"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py new file mode 100644 index 0000000..4e13cb8 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py @@ -0,0 +1,116 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) +from typing_extensions import Annotated + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class GeoJSONMultiLineString(BaseModel): + """ + GeoJSONMultiLineString + """ # noqa: E501 + + type: StrictStr + coordinates: List[ + Annotated[ + List[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)]], + Field(min_length=2), + ] + ] + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None + __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] + + @field_validator("type") + def type_validate_enum(cls, value): + """Validates the enum""" + if value not in ("MultiLineString"): + raise ValueError("must be one of enum values ('MultiLineString')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of GeoJSONMultiLineString from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of GeoJSONMultiLineString from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "type": obj.get("type"), + "coordinates": obj.get("coordinates"), + "bbox": obj.get("bbox"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py new file mode 100644 index 0000000..8d64573 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py @@ -0,0 +1,113 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) +from typing_extensions import Annotated + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class GeoJSONMultiPoint(BaseModel): + """ + GeoJSONMultiPoint + """ # noqa: E501 + + type: StrictStr + coordinates: List[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)] + ] + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None + __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] + + @field_validator("type") + def type_validate_enum(cls, value): + """Validates the enum""" + if value not in ("MultiPoint"): + raise ValueError("must be one of enum values ('MultiPoint')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of GeoJSONMultiPoint from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of GeoJSONMultiPoint from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "type": obj.get("type"), + "coordinates": obj.get("coordinates"), + "bbox": obj.get("bbox"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py new file mode 100644 index 0000000..56775ac --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py @@ -0,0 +1,120 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) +from typing_extensions import Annotated + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class GeoJSONMultiPolygon(BaseModel): + """ + GeoJSONMultiPolygon + """ # noqa: E501 + + type: StrictStr + coordinates: List[ + List[ + Annotated[ + List[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)] + ], + Field(min_length=4), + ] + ] + ] + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None + __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] + + @field_validator("type") + def type_validate_enum(cls, value): + """Validates the enum""" + if value not in ("MultiPolygon"): + raise ValueError("must be one of enum values ('MultiPolygon')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of GeoJSONMultiPolygon from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of GeoJSONMultiPolygon from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "type": obj.get("type"), + "coordinates": obj.get("coordinates"), + "bbox": obj.get("bbox"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_point.py b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_point.py new file mode 100644 index 0000000..c8eb97f --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_point.py @@ -0,0 +1,111 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) +from typing_extensions import Annotated + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class GeoJSONPoint(BaseModel): + """ + GeoJSONPoint + """ # noqa: E501 + + type: StrictStr + coordinates: Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)] + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None + __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] + + @field_validator("type") + def type_validate_enum(cls, value): + """Validates the enum""" + if value not in ("Point"): + raise ValueError("must be one of enum values ('Point')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of GeoJSONPoint from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of GeoJSONPoint from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "type": obj.get("type"), + "coordinates": obj.get("coordinates"), + "bbox": obj.get("bbox"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py new file mode 100644 index 0000000..c535824 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py @@ -0,0 +1,116 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) +from typing_extensions import Annotated + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class GeoJSONPolygon(BaseModel): + """ + GeoJSONPolygon + """ # noqa: E501 + + type: StrictStr + coordinates: List[ + Annotated[ + List[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)]], + Field(min_length=4), + ] + ] + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None + __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] + + @field_validator("type") + def type_validate_enum(cls, value): + """Validates the enum""" + if value not in ("Polygon"): + raise ValueError("must be one of enum values ('Polygon')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of GeoJSONPolygon from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of GeoJSONPolygon from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "type": obj.get("type"), + "coordinates": obj.get("coordinates"), + "bbox": obj.get("bbox"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py b/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py new file mode 100644 index 0000000..4711b4b --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py @@ -0,0 +1,184 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.input_value_no_object import InputValueNoObject +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.qualified_input_value import QualifiedInputValue + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +INLINEORREFDATA_ONE_OF_SCHEMAS = ["InputValueNoObject", "Link", "QualifiedInputValue"] + + +class InlineOrRefData(BaseModel): + """ + InlineOrRefData + """ + + # data type: InputValueNoObject + oneof_schema_1_validator: Optional[InputValueNoObject] = None + # data type: QualifiedInputValue + oneof_schema_2_validator: Optional[QualifiedInputValue] = None + # data type: Link + oneof_schema_3_validator: Optional[Link] = None + actual_instance: Optional[Union[InputValueNoObject, Link, QualifiedInputValue]] = ( + None + ) + one_of_schemas: List[str] = Literal[ + "InputValueNoObject", "Link", "QualifiedInputValue" + ] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + InlineOrRefData.model_construct() + error_messages = [] + match = 0 + # validate data type: InputValueNoObject + if not isinstance(v, InputValueNoObject): + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputValueNoObject`" + ) + else: + match += 1 + # validate data type: QualifiedInputValue + if not isinstance(v, QualifiedInputValue): + error_messages.append( + f"Error! Input type `{type(v)}` is not `QualifiedInputValue`" + ) + else: + match += 1 + # validate data type: Link + if not isinstance(v, Link): + error_messages.append(f"Error! Input type `{type(v)}` is not `Link`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in InlineOrRefData with oneOf schemas: InputValueNoObject, Link, QualifiedInputValue. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in InlineOrRefData with oneOf schemas: InputValueNoObject, Link, QualifiedInputValue. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into InputValueNoObject + try: + instance.actual_instance = InputValueNoObject.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into QualifiedInputValue + try: + instance.actual_instance = QualifiedInputValue.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into Link + try: + instance.actual_instance = Link.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into InlineOrRefData with oneOf schemas: InputValueNoObject, Link, QualifiedInputValue. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into InlineOrRefData with oneOf schemas: InputValueNoObject, Link, QualifiedInputValue. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py b/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py new file mode 100644 index 0000000..0bf5526 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py @@ -0,0 +1,192 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.input_value_no_object1 import ( + InputValueNoObject1, +) +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.qualified_input_value1 import ( + QualifiedInputValue1, +) + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +INLINEORREFDATA1_ONE_OF_SCHEMAS = [ + "InputValueNoObject1", + "Link", + "QualifiedInputValue1", +] + + +class InlineOrRefData1(BaseModel): + """ + InlineOrRefData1 + """ + + # data type: InputValueNoObject1 + oneof_schema_1_validator: Optional[InputValueNoObject1] = None + # data type: QualifiedInputValue1 + oneof_schema_2_validator: Optional[QualifiedInputValue1] = None + # data type: Link + oneof_schema_3_validator: Optional[Link] = None + actual_instance: Optional[ + Union[InputValueNoObject1, Link, QualifiedInputValue1] + ] = None + one_of_schemas: List[str] = Literal[ + "InputValueNoObject1", "Link", "QualifiedInputValue1" + ] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + InlineOrRefData1.model_construct() + error_messages = [] + match = 0 + # validate data type: InputValueNoObject1 + if not isinstance(v, InputValueNoObject1): + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputValueNoObject1`" + ) + else: + match += 1 + # validate data type: QualifiedInputValue1 + if not isinstance(v, QualifiedInputValue1): + error_messages.append( + f"Error! Input type `{type(v)}` is not `QualifiedInputValue1`" + ) + else: + match += 1 + # validate data type: Link + if not isinstance(v, Link): + error_messages.append(f"Error! Input type `{type(v)}` is not `Link`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in InlineOrRefData1 with oneOf schemas: InputValueNoObject1, Link, QualifiedInputValue1. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in InlineOrRefData1 with oneOf schemas: InputValueNoObject1, Link, QualifiedInputValue1. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into InputValueNoObject1 + try: + instance.actual_instance = InputValueNoObject1.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into QualifiedInputValue1 + try: + instance.actual_instance = QualifiedInputValue1.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into Link + try: + instance.actual_instance = Link.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into InlineOrRefData1 with oneOf schemas: InputValueNoObject1, Link, QualifiedInputValue1. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into InlineOrRefData1 with oneOf schemas: InputValueNoObject1, Link, QualifiedInputValue1. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py b/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py new file mode 100644 index 0000000..7714621 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py @@ -0,0 +1,197 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.link import Link + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +INLINEORREFDATAWORKFLOWS_ONE_OF_SCHEMAS = [ + "InputValueNoObjectWorkflows", + "Link", + "QualifiedInputValueWorkflows", +] + + +class InlineOrRefDataWorkflows(BaseModel): + """ + InlineOrRefDataWorkflows + """ + + # data type: InputValueNoObjectWorkflows + oneof_schema_1_validator: Optional[InputValueNoObjectWorkflows] = None + # data type: QualifiedInputValueWorkflows + oneof_schema_2_validator: Optional[QualifiedInputValueWorkflows] = None + # data type: Link + oneof_schema_3_validator: Optional[Link] = None + actual_instance: Optional[ + Union[InputValueNoObjectWorkflows, Link, QualifiedInputValueWorkflows] + ] = None + one_of_schemas: List[str] = Literal[ + "InputValueNoObjectWorkflows", "Link", "QualifiedInputValueWorkflows" + ] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + InlineOrRefDataWorkflows.model_construct() + error_messages = [] + match = 0 + # validate data type: InputValueNoObjectWorkflows + if not isinstance(v, InputValueNoObjectWorkflows): + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputValueNoObjectWorkflows`" + ) + else: + match += 1 + # validate data type: QualifiedInputValueWorkflows + if not isinstance(v, QualifiedInputValueWorkflows): + error_messages.append( + f"Error! Input type `{type(v)}` is not `QualifiedInputValueWorkflows`" + ) + else: + match += 1 + # validate data type: Link + if not isinstance(v, Link): + error_messages.append(f"Error! Input type `{type(v)}` is not `Link`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in InlineOrRefDataWorkflows with oneOf schemas: InputValueNoObjectWorkflows, Link, QualifiedInputValueWorkflows. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in InlineOrRefDataWorkflows with oneOf schemas: InputValueNoObjectWorkflows, Link, QualifiedInputValueWorkflows. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into InputValueNoObjectWorkflows + try: + instance.actual_instance = InputValueNoObjectWorkflows.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into QualifiedInputValueWorkflows + try: + instance.actual_instance = QualifiedInputValueWorkflows.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into Link + try: + instance.actual_instance = Link.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into InlineOrRefDataWorkflows with oneOf schemas: InputValueNoObjectWorkflows, Link, QualifiedInputValueWorkflows. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into InlineOrRefDataWorkflows with oneOf schemas: InputValueNoObjectWorkflows, Link, QualifiedInputValueWorkflows. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + +from unity_sps_ogc_processes_api.models.input_value_no_object_workflows import ( + InputValueNoObjectWorkflows, +) +from unity_sps_ogc_processes_api.models.qualified_input_value_workflows import ( + QualifiedInputValueWorkflows, +) + +# TODO: Rewrite to not use raise_errors +InlineOrRefDataWorkflows.model_rebuild(raise_errors=False) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input.py b/app-cp/src/unity_sps_ogc_processes_api/models/input.py new file mode 100644 index 0000000..7e695d8 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input.py @@ -0,0 +1,167 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.inline_or_ref_data1 import InlineOrRefData1 + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +INPUT_ONE_OF_SCHEMAS = ["InlineOrRefData1", "List[InlineOrRefData1]"] + + +class Input(BaseModel): + """ + Input + """ + + # data type: InlineOrRefData1 + oneof_schema_1_validator: Optional[InlineOrRefData1] = None + # data type: List[InlineOrRefData1] + oneof_schema_2_validator: Optional[List[InlineOrRefData1]] = None + actual_instance: Optional[Union[InlineOrRefData1, List[InlineOrRefData1]]] = None + one_of_schemas: List[str] = Literal["InlineOrRefData1", "List[InlineOrRefData1]"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = Input.model_construct() + error_messages = [] + match = 0 + # validate data type: InlineOrRefData1 + if not isinstance(v, InlineOrRefData1): + error_messages.append( + f"Error! Input type `{type(v)}` is not `InlineOrRefData1`" + ) + else: + match += 1 + # validate data type: List[InlineOrRefData1] + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in Input with oneOf schemas: InlineOrRefData1, List[InlineOrRefData1]. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in Input with oneOf schemas: InlineOrRefData1, List[InlineOrRefData1]. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into InlineOrRefData1 + try: + instance.actual_instance = InlineOrRefData1.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into List[InlineOrRefData1] + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into Input with oneOf schemas: InlineOrRefData1, List[InlineOrRefData1]. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into Input with oneOf schemas: InlineOrRefData1, List[InlineOrRefData1]. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_collection.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_collection.py new file mode 100644 index 0000000..fa243d3 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_collection.py @@ -0,0 +1,107 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( + FieldsModifiersProperties, +) + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class InputCollection(BaseModel): + """ + InputCollection + """ # noqa: E501 + + filter: Optional[StrictStr] = None + properties: Optional[FieldsModifiersProperties] = None + sort_by: Optional[List[StrictStr]] = Field(default=None, alias="sortBy") + collection: StrictStr + __properties: ClassVar[List[str]] = ["filter", "properties", "sortBy", "collection"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of InputCollection from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of properties + if self.properties: + _dict["properties"] = self.properties.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of InputCollection from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "filter": obj.get("filter"), + "properties": ( + FieldsModifiersProperties.from_dict(obj.get("properties")) + if obj.get("properties") is not None + else None + ), + "sortBy": obj.get("sortBy"), + "collection": obj.get("collection"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_description.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_description.py new file mode 100644 index 0000000..eb803ce --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_description.py @@ -0,0 +1,161 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictInt, StrictStr, field_validator + +from unity_sps_ogc_processes_api.models.input_description_all_of_max_occurs import ( + InputDescriptionAllOfMaxOccurs, +) +from unity_sps_ogc_processes_api.models.metadata import Metadata +from unity_sps_ogc_processes_api.models.model_schema import ModelSchema + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class InputDescription(BaseModel): + """ + InputDescription + """ # noqa: E501 + + title: Optional[StrictStr] = None + description: Optional[StrictStr] = None + keywords: Optional[List[StrictStr]] = None + metadata: Optional[List[Metadata]] = None + schema: ModelSchema = Field(alias="schema") + min_occurs: Optional[StrictInt] = Field(default=1, alias="minOccurs") + max_occurs: Optional[InputDescriptionAllOfMaxOccurs] = Field( + default=None, alias="maxOccurs" + ) + value_passing: Optional[List[StrictStr]] = Field(default=None, alias="valuePassing") + __properties: ClassVar[List[str]] = [ + "title", + "description", + "keywords", + "metadata", + "schema", + "minOccurs", + "maxOccurs", + "valuePassing", + ] + + @field_validator("value_passing") + def value_passing_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + for i in value: + if i not in ("byValue", "byReference"): + raise ValueError( + "each list item must be one of ('byValue', 'byReference')" + ) + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of InputDescription from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in metadata (list) + _items = [] + if self.metadata: + for _item in self.metadata: + if _item: + _items.append(_item.to_dict()) + _dict["metadata"] = _items + # override the default output from pydantic by calling `to_dict()` of schema + if self.schema: + _dict["schema"] = self.schema.to_dict() + # override the default output from pydantic by calling `to_dict()` of max_occurs + if self.max_occurs: + _dict["maxOccurs"] = self.max_occurs.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of InputDescription from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "title": obj.get("title"), + "description": obj.get("description"), + "keywords": obj.get("keywords"), + "metadata": ( + [Metadata.from_dict(_item) for _item in obj.get("metadata")] + if obj.get("metadata") is not None + else None + ), + "schema": ( + ModelSchema.from_dict(obj.get("schema")) + if obj.get("schema") is not None + else None + ), + "minOccurs": ( + obj.get("minOccurs") if obj.get("minOccurs") is not None else 1 + ), + "maxOccurs": ( + InputDescriptionAllOfMaxOccurs.from_dict(obj.get("maxOccurs")) + if obj.get("maxOccurs") is not None + else None + ), + "valuePassing": obj.get("valuePassing"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py new file mode 100644 index 0000000..20d2005 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py @@ -0,0 +1,167 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, StrictInt, StrictStr, ValidationError, field_validator +from typing_extensions import Literal + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +INPUTDESCRIPTIONALLOFMAXOCCURS_ONE_OF_SCHEMAS = ["int", "str"] + + +class InputDescriptionAllOfMaxOccurs(BaseModel): + """ + InputDescriptionAllOfMaxOccurs + """ + + # data type: int + oneof_schema_1_validator: Optional[StrictInt] = 1 + # data type: str + oneof_schema_2_validator: Optional[StrictStr] = None + actual_instance: Optional[Union[int, str]] = None + one_of_schemas: List[str] = Literal["int", "str"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = InputDescriptionAllOfMaxOccurs.model_construct() + error_messages = [] + match = 0 + # validate data type: int + try: + instance.oneof_schema_1_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: str + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in InputDescriptionAllOfMaxOccurs with oneOf schemas: int, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in InputDescriptionAllOfMaxOccurs with oneOf schemas: int, str. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into int + try: + # validation + instance.oneof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_1_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into str + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into InputDescriptionAllOfMaxOccurs with oneOf schemas: int, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into InputDescriptionAllOfMaxOccurs with oneOf schemas: int, str. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_parameterized.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_parameterized.py new file mode 100644 index 0000000..71b58f8 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_parameterized.py @@ -0,0 +1,107 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( + FieldsModifiersProperties, +) + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class InputParameterized(BaseModel): + """ + InputParameterized + """ # noqa: E501 + + filter: Optional[StrictStr] = None + properties: Optional[FieldsModifiersProperties] = None + sort_by: Optional[List[StrictStr]] = Field(default=None, alias="sortBy") + input: StrictStr = Field(alias="$input") + __properties: ClassVar[List[str]] = ["filter", "properties", "sortBy", "$input"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of InputParameterized from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of properties + if self.properties: + _dict["properties"] = self.properties.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of InputParameterized from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "filter": obj.get("filter"), + "properties": ( + FieldsModifiersProperties.from_dict(obj.get("properties")) + if obj.get("properties") is not None + else None + ), + "sortBy": obj.get("sortBy"), + "$input": obj.get("$input"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_process.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_process.py new file mode 100644 index 0000000..e80c881 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_process.py @@ -0,0 +1,166 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( + FieldsModifiersProperties, +) +from unity_sps_ogc_processes_api.models.output_workflows1 import OutputWorkflows1 +from unity_sps_ogc_processes_api.models.subscriber import Subscriber + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class InputProcess(BaseModel): + """ + InputProcess + """ # noqa: E501 + + process: StrictStr = Field( + description="URI to the process execution end point (i.e., `.../processes/{processId}/execution`)" + ) + inputs: Optional[Dict[str, InputWorkflows1]] = None + outputs: Optional[Dict[str, OutputWorkflows1]] = None + subscriber: Optional[Subscriber] = None + filter: Optional[StrictStr] = None + properties: Optional[FieldsModifiersProperties] = None + sort_by: Optional[List[StrictStr]] = Field(default=None, alias="sortBy") + __properties: ClassVar[List[str]] = [ + "process", + "inputs", + "outputs", + "subscriber", + "filter", + "properties", + "sortBy", + ] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of InputProcess from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each value in inputs (dict) + _field_dict = {} + if self.inputs: + for _key in self.inputs: + if self.inputs[_key]: + _field_dict[_key] = self.inputs[_key].to_dict() + _dict["inputs"] = _field_dict + # override the default output from pydantic by calling `to_dict()` of each value in outputs (dict) + _field_dict = {} + if self.outputs: + for _key in self.outputs: + if self.outputs[_key]: + _field_dict[_key] = self.outputs[_key].to_dict() + _dict["outputs"] = _field_dict + # override the default output from pydantic by calling `to_dict()` of subscriber + if self.subscriber: + _dict["subscriber"] = self.subscriber.to_dict() + # override the default output from pydantic by calling `to_dict()` of properties + if self.properties: + _dict["properties"] = self.properties.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of InputProcess from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "process": obj.get("process"), + "inputs": ( + dict( + (_k, InputWorkflows1.from_dict(_v)) + for _k, _v in obj.get("inputs").items() + ) + if obj.get("inputs") is not None + else None + ), + "outputs": ( + dict( + (_k, OutputWorkflows1.from_dict(_v)) + for _k, _v in obj.get("outputs").items() + ) + if obj.get("outputs") is not None + else None + ), + "subscriber": ( + Subscriber.from_dict(obj.get("subscriber")) + if obj.get("subscriber") is not None + else None + ), + "filter": obj.get("filter"), + "properties": ( + FieldsModifiersProperties.from_dict(obj.get("properties")) + if obj.get("properties") is not None + else None + ), + "sortBy": obj.get("sortBy"), + } + ) + return _obj + + +from unity_sps_ogc_processes_api.models.input_workflows1 import InputWorkflows1 + +# TODO: Rewrite to not use raise_errors +InputProcess.model_rebuild(raise_errors=False) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_value.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_value.py new file mode 100644 index 0000000..aec7958 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_value.py @@ -0,0 +1,155 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.input_value_no_object import InputValueNoObject + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +INPUTVALUE_ANY_OF_SCHEMAS = ["InputValueNoObject", "object"] + + +class InputValue(BaseModel): + """ + InputValue + """ + + # data type: InputValueNoObject + anyof_schema_1_validator: Optional[InputValueNoObject] = None + # data type: object + anyof_schema_2_validator: Optional[Dict[str, Any]] = None + if TYPE_CHECKING: + actual_instance: Optional[Union[InputValueNoObject, object]] = None + else: + actual_instance: Any = None + any_of_schemas: List[str] = Literal[INPUTVALUE_ANY_OF_SCHEMAS] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_anyof(cls, v): + instance = InputValue.model_construct() + error_messages = [] + # validate data type: InputValueNoObject + if not isinstance(v, InputValueNoObject): + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputValueNoObject`" + ) + else: + return v + + # validate data type: object + try: + instance.anyof_schema_2_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if error_messages: + # no match + raise ValueError( + "No match found when setting the actual_instance in InputValue with anyOf schemas: InputValueNoObject, object. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + # anyof_schema_1_validator: Optional[InputValueNoObject] = None + try: + instance.actual_instance = InputValueNoObject.from_json(json_str) + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into object + try: + # validation + instance.anyof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_2_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if error_messages: + # no match + raise ValueError( + "No match found when deserializing the JSON string into InputValue with anyOf schemas: InputValueNoObject, object. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_dict() + else: + return json.dumps(self.actual_instance) + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_value1.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_value1.py new file mode 100644 index 0000000..e51aafd --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_value1.py @@ -0,0 +1,157 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.input_value_no_object1 import ( + InputValueNoObject1, +) + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +INPUTVALUE1_ANY_OF_SCHEMAS = ["InputValueNoObject1", "object"] + + +class InputValue1(BaseModel): + """ + InputValue1 + """ + + # data type: InputValueNoObject1 + anyof_schema_1_validator: Optional[InputValueNoObject1] = None + # data type: object + anyof_schema_2_validator: Optional[Dict[str, Any]] = None + if TYPE_CHECKING: + actual_instance: Optional[Union[InputValueNoObject1, object]] = None + else: + actual_instance: Any = None + any_of_schemas: List[str] = Literal[INPUTVALUE1_ANY_OF_SCHEMAS] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_anyof(cls, v): + instance = InputValue1.model_construct() + error_messages = [] + # validate data type: InputValueNoObject1 + if not isinstance(v, InputValueNoObject1): + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputValueNoObject1`" + ) + else: + return v + + # validate data type: object + try: + instance.anyof_schema_2_validator = v + return v + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if error_messages: + # no match + raise ValueError( + "No match found when setting the actual_instance in InputValue1 with anyOf schemas: InputValueNoObject1, object. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + # anyof_schema_1_validator: Optional[InputValueNoObject1] = None + try: + instance.actual_instance = InputValueNoObject1.from_json(json_str) + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into object + try: + # validation + instance.anyof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.anyof_schema_2_validator + return instance + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if error_messages: + # no match + raise ValueError( + "No match found when deserializing the JSON string into InputValue1 with anyOf schemas: InputValueNoObject1, object. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_dict() + else: + return json.dumps(self.actual_instance) + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object.py new file mode 100644 index 0000000..fc969fd --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object.py @@ -0,0 +1,268 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + StrictBool, + StrictBytes, + StrictFloat, + StrictInt, + StrictStr, + ValidationError, + field_validator, +) +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.bbox import Bbox + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +INPUTVALUENOOBJECT_ONE_OF_SCHEMAS = [ + "Bbox", + "List[object]", + "bool", + "float", + "int", + "str", +] + + +class InputValueNoObject(BaseModel): + """ + InputValueNoObject + """ + + # data type: str + oneof_schema_1_validator: Optional[StrictStr] = None + # data type: float + oneof_schema_2_validator: Optional[Union[StrictFloat, StrictInt]] = None + # data type: int + oneof_schema_3_validator: Optional[StrictInt] = None + # data type: bool + oneof_schema_4_validator: Optional[StrictBool] = None + # data type: List[object] + oneof_schema_5_validator: Optional[List[Dict[str, Any]]] = None + # data type: str + oneof_schema_6_validator: Optional[Union[StrictBytes, StrictStr]] = None + # data type: Bbox + oneof_schema_7_validator: Optional[Bbox] = None + actual_instance: Optional[Union[Bbox, List[object], bool, float, int, str]] = None + one_of_schemas: List[str] = Literal[ + "Bbox", "List[object]", "bool", "float", "int", "str" + ] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = InputValueNoObject.model_construct() + error_messages = [] + match = 0 + # validate data type: str + try: + instance.oneof_schema_1_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: float + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: int + try: + instance.oneof_schema_3_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: bool + try: + instance.oneof_schema_4_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: List[object] + try: + instance.oneof_schema_5_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: str + try: + instance.oneof_schema_6_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: Bbox + if not isinstance(v, Bbox): + error_messages.append(f"Error! Input type `{type(v)}` is not `Bbox`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in InputValueNoObject with oneOf schemas: Bbox, List[object], bool, float, int, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in InputValueNoObject with oneOf schemas: Bbox, List[object], bool, float, int, str. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into str + try: + # validation + instance.oneof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_1_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into float + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into int + try: + # validation + instance.oneof_schema_3_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_3_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into bool + try: + # validation + instance.oneof_schema_4_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_4_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into List[object] + try: + # validation + instance.oneof_schema_5_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_5_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into str + try: + # validation + instance.oneof_schema_6_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_6_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into Bbox + try: + instance.actual_instance = Bbox.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into InputValueNoObject with oneOf schemas: Bbox, List[object], bool, float, int, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into InputValueNoObject with oneOf schemas: Bbox, List[object], bool, float, int, str. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py new file mode 100644 index 0000000..9dd2681 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py @@ -0,0 +1,268 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + StrictBool, + StrictBytes, + StrictFloat, + StrictInt, + StrictStr, + ValidationError, + field_validator, +) +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.bbox1 import Bbox1 + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +INPUTVALUENOOBJECT1_ONE_OF_SCHEMAS = [ + "Bbox1", + "List[object]", + "bool", + "float", + "int", + "str", +] + + +class InputValueNoObject1(BaseModel): + """ + InputValueNoObject1 + """ + + # data type: str + oneof_schema_1_validator: Optional[StrictStr] = None + # data type: float + oneof_schema_2_validator: Optional[Union[StrictFloat, StrictInt]] = None + # data type: int + oneof_schema_3_validator: Optional[StrictInt] = None + # data type: bool + oneof_schema_4_validator: Optional[StrictBool] = None + # data type: List[object] + oneof_schema_5_validator: Optional[List[Dict[str, Any]]] = None + # data type: str + oneof_schema_6_validator: Optional[Union[StrictBytes, StrictStr]] = None + # data type: Bbox1 + oneof_schema_7_validator: Optional[Bbox1] = None + actual_instance: Optional[Union[Bbox1, List[object], bool, float, int, str]] = None + one_of_schemas: List[str] = Literal[ + "Bbox1", "List[object]", "bool", "float", "int", "str" + ] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = InputValueNoObject1.model_construct() + error_messages = [] + match = 0 + # validate data type: str + try: + instance.oneof_schema_1_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: float + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: int + try: + instance.oneof_schema_3_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: bool + try: + instance.oneof_schema_4_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: List[object] + try: + instance.oneof_schema_5_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: str + try: + instance.oneof_schema_6_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: Bbox1 + if not isinstance(v, Bbox1): + error_messages.append(f"Error! Input type `{type(v)}` is not `Bbox1`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in InputValueNoObject1 with oneOf schemas: Bbox1, List[object], bool, float, int, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in InputValueNoObject1 with oneOf schemas: Bbox1, List[object], bool, float, int, str. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into str + try: + # validation + instance.oneof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_1_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into float + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into int + try: + # validation + instance.oneof_schema_3_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_3_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into bool + try: + # validation + instance.oneof_schema_4_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_4_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into List[object] + try: + # validation + instance.oneof_schema_5_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_5_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into str + try: + # validation + instance.oneof_schema_6_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_6_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into Bbox1 + try: + instance.actual_instance = Bbox1.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into InputValueNoObject1 with oneOf schemas: Bbox1, List[object], bool, float, int, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into InputValueNoObject1 with oneOf schemas: Bbox1, List[object], bool, float, int, str. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py new file mode 100644 index 0000000..fc49e7e --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py @@ -0,0 +1,344 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + StrictBool, + StrictBytes, + StrictFloat, + StrictInt, + StrictStr, + ValidationError, + field_validator, +) +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.bbox1 import Bbox1 +from unity_sps_ogc_processes_api.models.input_collection import InputCollection +from unity_sps_ogc_processes_api.models.input_parameterized import InputParameterized + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +INPUTVALUENOOBJECTWORKFLOWS_ONE_OF_SCHEMAS = [ + "Bbox1", + "InputCollection", + "InputParameterized", + "InputProcess", + "List[object]", + "bool", + "float", + "int", + "str", +] + + +class InputValueNoObjectWorkflows(BaseModel): + """ + InputValueNoObjectWorkflows + """ + + # data type: str + oneof_schema_1_validator: Optional[StrictStr] = None + # data type: float + oneof_schema_2_validator: Optional[Union[StrictFloat, StrictInt]] = None + # data type: int + oneof_schema_3_validator: Optional[StrictInt] = None + # data type: bool + oneof_schema_4_validator: Optional[StrictBool] = None + # data type: List[object] + oneof_schema_5_validator: Optional[List[Dict[str, Any]]] = None + # data type: str + oneof_schema_6_validator: Optional[Union[StrictBytes, StrictStr]] = None + # data type: Bbox1 + oneof_schema_7_validator: Optional[Bbox1] = None + # data type: InputCollection + oneof_schema_8_validator: Optional[InputCollection] = None + # data type: InputProcess + oneof_schema_9_validator: Optional[InputProcess] = None + # data type: InputParameterized + oneof_schema_10_validator: Optional[InputParameterized] = None + actual_instance: Optional[ + Union[ + Bbox1, + InputCollection, + InputParameterized, + InputProcess, + List[object], + bool, + float, + int, + str, + ] + ] = None + one_of_schemas: List[str] = Literal[ + "Bbox1", + "InputCollection", + "InputParameterized", + "InputProcess", + "List[object]", + "bool", + "float", + "int", + "str", + ] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = InputValueNoObjectWorkflows.model_construct() + error_messages = [] + match = 0 + # validate data type: str + try: + instance.oneof_schema_1_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: float + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: int + try: + instance.oneof_schema_3_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: bool + try: + instance.oneof_schema_4_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: List[object] + try: + instance.oneof_schema_5_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: str + try: + instance.oneof_schema_6_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: Bbox1 + if not isinstance(v, Bbox1): + error_messages.append(f"Error! Input type `{type(v)}` is not `Bbox1`") + else: + match += 1 + # validate data type: InputCollection + if not isinstance(v, InputCollection): + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputCollection`" + ) + else: + match += 1 + # validate data type: InputProcess + if not isinstance(v, InputProcess): + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputProcess`" + ) + else: + match += 1 + # validate data type: InputParameterized + if not isinstance(v, InputParameterized): + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputParameterized`" + ) + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in InputValueNoObjectWorkflows with oneOf schemas: Bbox1, InputCollection, InputParameterized, InputProcess, List[object], bool, float, int, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in InputValueNoObjectWorkflows with oneOf schemas: Bbox1, InputCollection, InputParameterized, InputProcess, List[object], bool, float, int, str. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into str + try: + # validation + instance.oneof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_1_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into float + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into int + try: + # validation + instance.oneof_schema_3_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_3_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into bool + try: + # validation + instance.oneof_schema_4_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_4_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into List[object] + try: + # validation + instance.oneof_schema_5_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_5_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into str + try: + # validation + instance.oneof_schema_6_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_6_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into Bbox1 + try: + instance.actual_instance = Bbox1.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into InputCollection + try: + instance.actual_instance = InputCollection.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into InputProcess + try: + instance.actual_instance = InputProcess.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into InputParameterized + try: + instance.actual_instance = InputParameterized.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into InputValueNoObjectWorkflows with oneOf schemas: Bbox1, InputCollection, InputParameterized, InputProcess, List[object], bool, float, int, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into InputValueNoObjectWorkflows with oneOf schemas: Bbox1, InputCollection, InputParameterized, InputProcess, List[object], bool, float, int, str. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + +from unity_sps_ogc_processes_api.models.input_process import InputProcess + +# TODO: Rewrite to not use raise_errors +InputValueNoObjectWorkflows.model_rebuild(raise_errors=False) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_value_workflows.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_value_workflows.py new file mode 100644 index 0000000..6d18d3a --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_value_workflows.py @@ -0,0 +1,173 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +INPUTVALUEWORKFLOWS_ONE_OF_SCHEMAS = ["InputValueNoObjectWorkflows", "object"] + + +class InputValueWorkflows(BaseModel): + """ + InputValueWorkflows + """ + + # data type: InputValueNoObjectWorkflows + oneof_schema_1_validator: Optional[InputValueNoObjectWorkflows] = None + # data type: object + oneof_schema_2_validator: Optional[Dict[str, Any]] = None + actual_instance: Optional[Union[InputValueNoObjectWorkflows, object]] = None + one_of_schemas: List[str] = Literal["InputValueNoObjectWorkflows", "object"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = InputValueWorkflows.model_construct() + error_messages = [] + match = 0 + # validate data type: InputValueNoObjectWorkflows + if not isinstance(v, InputValueNoObjectWorkflows): + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputValueNoObjectWorkflows`" + ) + else: + match += 1 + # validate data type: object + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in InputValueWorkflows with oneOf schemas: InputValueNoObjectWorkflows, object. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in InputValueWorkflows with oneOf schemas: InputValueNoObjectWorkflows, object. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into InputValueNoObjectWorkflows + try: + instance.actual_instance = InputValueNoObjectWorkflows.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into object + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into InputValueWorkflows with oneOf schemas: InputValueNoObjectWorkflows, object. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into InputValueWorkflows with oneOf schemas: InputValueNoObjectWorkflows, object. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + +from unity_sps_ogc_processes_api.models.input_value_no_object_workflows import ( + InputValueNoObjectWorkflows, +) + +# TODO: Rewrite to not use raise_errors +InputValueWorkflows.model_rebuild(raise_errors=False) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_workflows.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_workflows.py new file mode 100644 index 0000000..11a9420 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_workflows.py @@ -0,0 +1,176 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.inline_or_ref_data_workflows import ( + InlineOrRefDataWorkflows, +) + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +INPUTWORKFLOWS_ONE_OF_SCHEMAS = [ + "InlineOrRefDataWorkflows", + "List[InlineOrRefDataWorkflows]", +] + + +class InputWorkflows(BaseModel): + """ + InputWorkflows + """ + + # data type: InlineOrRefDataWorkflows + oneof_schema_1_validator: Optional[InlineOrRefDataWorkflows] = None + # data type: List[InlineOrRefDataWorkflows] + oneof_schema_2_validator: Optional[List[InlineOrRefDataWorkflows]] = None + actual_instance: Optional[ + Union[InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]] + ] = None + one_of_schemas: List[str] = Literal[ + "InlineOrRefDataWorkflows", "List[InlineOrRefDataWorkflows]" + ] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = InputWorkflows.model_construct() + error_messages = [] + match = 0 + # validate data type: InlineOrRefDataWorkflows + if not isinstance(v, InlineOrRefDataWorkflows): + error_messages.append( + f"Error! Input type `{type(v)}` is not `InlineOrRefDataWorkflows`" + ) + else: + match += 1 + # validate data type: List[InlineOrRefDataWorkflows] + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in InputWorkflows with oneOf schemas: InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in InputWorkflows with oneOf schemas: InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into InlineOrRefDataWorkflows + try: + instance.actual_instance = InlineOrRefDataWorkflows.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into List[InlineOrRefDataWorkflows] + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into InputWorkflows with oneOf schemas: InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into InputWorkflows with oneOf schemas: InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_workflows1.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_workflows1.py new file mode 100644 index 0000000..6dd9a1e --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_workflows1.py @@ -0,0 +1,180 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +INPUTWORKFLOWS1_ONE_OF_SCHEMAS = [ + "InlineOrRefDataWorkflows", + "List[InlineOrRefDataWorkflows]", +] + + +class InputWorkflows1(BaseModel): + """ + InputWorkflows1 + """ + + # data type: InlineOrRefDataWorkflows + oneof_schema_1_validator: Optional[InlineOrRefDataWorkflows] = None + # data type: List[InlineOrRefDataWorkflows] + oneof_schema_2_validator: Optional[List[InlineOrRefDataWorkflows]] = None + actual_instance: Optional[ + Union[InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]] + ] = None + one_of_schemas: List[str] = Literal[ + "InlineOrRefDataWorkflows", "List[InlineOrRefDataWorkflows]" + ] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = InputWorkflows1.model_construct() + error_messages = [] + match = 0 + # validate data type: InlineOrRefDataWorkflows + if not isinstance(v, InlineOrRefDataWorkflows): + error_messages.append( + f"Error! Input type `{type(v)}` is not `InlineOrRefDataWorkflows`" + ) + else: + match += 1 + # validate data type: List[InlineOrRefDataWorkflows] + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in InputWorkflows1 with oneOf schemas: InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in InputWorkflows1 with oneOf schemas: InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into InlineOrRefDataWorkflows + try: + instance.actual_instance = InlineOrRefDataWorkflows.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into List[InlineOrRefDataWorkflows] + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into InputWorkflows1 with oneOf schemas: InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into InputWorkflows1 with oneOf schemas: InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + +from unity_sps_ogc_processes_api.models.inline_or_ref_data_workflows import ( + InlineOrRefDataWorkflows, +) + +# TODO: Rewrite to not use raise_errors +InputWorkflows1.model_rebuild(raise_errors=False) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/job_control_options.py b/app-cp/src/unity_sps_ogc_processes_api/models/job_control_options.py new file mode 100644 index 0000000..da5d6d9 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/job_control_options.py @@ -0,0 +1,43 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import re # noqa: F401 +from enum import Enum + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class JobControlOptions(str, Enum): + """ + JobControlOptions + """ + + """ + allowed enum values + """ + SYNC_MINUS_EXECUTE = "sync-execute" + ASYNC_MINUS_EXECUTE = "async-execute" + DISMISS = "dismiss" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of JobControlOptions from a JSON string""" + return cls(json.loads(json_str)) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/job_list.py b/app-cp/src/unity_sps_ogc_processes_api/models/job_list.py new file mode 100644 index 0000000..9fe2f2f --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/job_list.py @@ -0,0 +1,117 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List + +from pydantic import BaseModel + +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.status_info import StatusInfo + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class JobList(BaseModel): + """ + JobList + """ # noqa: E501 + + jobs: List[StatusInfo] + links: List[Link] + __properties: ClassVar[List[str]] = ["jobs", "links"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of JobList from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in jobs (list) + _items = [] + if self.jobs: + for _item in self.jobs: + if _item: + _items.append(_item.to_dict()) + _dict["jobs"] = _items + # override the default output from pydantic by calling `to_dict()` of each item in links (list) + _items = [] + if self.links: + for _item in self.links: + if _item: + _items.append(_item.to_dict()) + _dict["links"] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of JobList from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "jobs": ( + [StatusInfo.from_dict(_item) for _item in obj.get("jobs")] + if obj.get("jobs") is not None + else None + ), + "links": ( + [Link.from_dict(_item) for _item in obj.get("links")] + if obj.get("links") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/landing_page.py b/app-cp/src/unity_sps_ogc_processes_api/models/landing_page.py new file mode 100644 index 0000000..2fffd7f --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/landing_page.py @@ -0,0 +1,112 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.link import Link + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class LandingPage(BaseModel): + """ + LandingPage + """ # noqa: E501 + + title: Optional[StrictStr] = None + description: Optional[StrictStr] = None + attribution: Optional[StrictStr] = Field( + default=None, + description="The `attribution` should be short and intended for presentation to a user, for example, in a corner of a map. Parts of the text can be links to other resources if additional information is needed. The string can include HTML markup.", + ) + links: List[Link] + __properties: ClassVar[List[str]] = ["title", "description", "attribution", "links"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of LandingPage from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in links (list) + _items = [] + if self.links: + for _item in self.links: + if _item: + _items.append(_item.to_dict()) + _dict["links"] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of LandingPage from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "title": obj.get("title"), + "description": obj.get("description"), + "attribution": obj.get("attribution"), + "links": ( + [Link.from_dict(_item) for _item in obj.get("links")] + if obj.get("links") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/link.py b/app-cp/src/unity_sps_ogc_processes_api/models/link.py new file mode 100644 index 0000000..94ddded --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/link.py @@ -0,0 +1,98 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, StrictStr + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Link(BaseModel): + """ + Link + """ # noqa: E501 + + href: StrictStr + rel: Optional[StrictStr] = None + type: Optional[StrictStr] = None + hreflang: Optional[StrictStr] = None + title: Optional[StrictStr] = None + __properties: ClassVar[List[str]] = ["href", "rel", "type", "hreflang", "title"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of Link from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of Link from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "href": obj.get("href"), + "rel": obj.get("rel"), + "type": obj.get("type"), + "hreflang": obj.get("hreflang"), + "title": obj.get("title"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/metadata.py b/app-cp/src/unity_sps_ogc_processes_api/models/metadata.py new file mode 100644 index 0000000..a50719d --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/metadata.py @@ -0,0 +1,166 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.metadata_one_of import MetadataOneOf +from unity_sps_ogc_processes_api.models.metadata_one_of1 import MetadataOneOf1 + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +METADATA_ONE_OF_SCHEMAS = ["MetadataOneOf", "MetadataOneOf1"] + + +class Metadata(BaseModel): + """ + Metadata + """ + + # data type: MetadataOneOf + oneof_schema_1_validator: Optional[MetadataOneOf] = None + # data type: MetadataOneOf1 + oneof_schema_2_validator: Optional[MetadataOneOf1] = None + actual_instance: Optional[Union[MetadataOneOf, MetadataOneOf1]] = None + one_of_schemas: List[str] = Literal["MetadataOneOf", "MetadataOneOf1"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + Metadata.model_construct() + error_messages = [] + match = 0 + # validate data type: MetadataOneOf + if not isinstance(v, MetadataOneOf): + error_messages.append( + f"Error! Input type `{type(v)}` is not `MetadataOneOf`" + ) + else: + match += 1 + # validate data type: MetadataOneOf1 + if not isinstance(v, MetadataOneOf1): + error_messages.append( + f"Error! Input type `{type(v)}` is not `MetadataOneOf1`" + ) + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in Metadata with oneOf schemas: MetadataOneOf, MetadataOneOf1. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in Metadata with oneOf schemas: MetadataOneOf, MetadataOneOf1. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into MetadataOneOf + try: + instance.actual_instance = MetadataOneOf.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into MetadataOneOf1 + try: + instance.actual_instance = MetadataOneOf1.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into Metadata with oneOf schemas: MetadataOneOf, MetadataOneOf1. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into Metadata with oneOf schemas: MetadataOneOf, MetadataOneOf1. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of.py b/app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of.py new file mode 100644 index 0000000..addb403 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of.py @@ -0,0 +1,107 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, StrictStr + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class MetadataOneOf(BaseModel): + """ + MetadataOneOf + """ # noqa: E501 + + href: StrictStr + rel: Optional[StrictStr] = None + type: Optional[StrictStr] = None + hreflang: Optional[StrictStr] = None + title: Optional[StrictStr] = None + role: Optional[StrictStr] = None + __properties: ClassVar[List[str]] = [ + "href", + "rel", + "type", + "hreflang", + "title", + "role", + ] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of MetadataOneOf from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of MetadataOneOf from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "href": obj.get("href"), + "rel": obj.get("rel"), + "type": obj.get("type"), + "hreflang": obj.get("hreflang"), + "title": obj.get("title"), + "role": obj.get("role"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py b/app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py new file mode 100644 index 0000000..cc3eeb3 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py @@ -0,0 +1,107 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, StrictStr + +from unity_sps_ogc_processes_api.models.metadata_one_of1_value import ( + MetadataOneOf1Value, +) + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class MetadataOneOf1(BaseModel): + """ + MetadataOneOf1 + """ # noqa: E501 + + role: Optional[StrictStr] = None + title: Optional[StrictStr] = None + lang: Optional[StrictStr] = None + value: Optional[MetadataOneOf1Value] = None + __properties: ClassVar[List[str]] = ["role", "title", "lang", "value"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of MetadataOneOf1 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of value + if self.value: + _dict["value"] = self.value.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of MetadataOneOf1 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "role": obj.get("role"), + "title": obj.get("title"), + "lang": obj.get("lang"), + "value": ( + MetadataOneOf1Value.from_dict(obj.get("value")) + if obj.get("value") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py b/app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py new file mode 100644 index 0000000..2930109 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py @@ -0,0 +1,167 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, Dict, List, Optional, Union + +from pydantic import BaseModel, StrictStr, ValidationError, field_validator +from typing_extensions import Literal + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +METADATAONEOF1VALUE_ONE_OF_SCHEMAS = ["object", "str"] + + +class MetadataOneOf1Value(BaseModel): + """ + MetadataOneOf1Value + """ + + # data type: str + oneof_schema_1_validator: Optional[StrictStr] = None + # data type: object + oneof_schema_2_validator: Optional[Dict[str, Any]] = None + actual_instance: Optional[Union[object, str]] = None + one_of_schemas: List[str] = Literal["object", "str"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = MetadataOneOf1Value.model_construct() + error_messages = [] + match = 0 + # validate data type: str + try: + instance.oneof_schema_1_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # validate data type: object + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in MetadataOneOf1Value with oneOf schemas: object, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in MetadataOneOf1Value with oneOf schemas: object, str. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into str + try: + # validation + instance.oneof_schema_1_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_1_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into object + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into MetadataOneOf1Value with oneOf schemas: object, str. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into MetadataOneOf1Value with oneOf schemas: object, str. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/model_schema.py b/app-cp/src/unity_sps_ogc_processes_api/models/model_schema.py new file mode 100644 index 0000000..0efcc3d --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/model_schema.py @@ -0,0 +1,162 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.reference import Reference +from unity_sps_ogc_processes_api.models.schema_one_of import SchemaOneOf + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +MODELSCHEMA_ONE_OF_SCHEMAS = ["Reference", "SchemaOneOf"] + + +class ModelSchema(BaseModel): + """ + ModelSchema + """ + + # data type: Reference + oneof_schema_1_validator: Optional[Reference] = None + # data type: SchemaOneOf + oneof_schema_2_validator: Optional[SchemaOneOf] = None + actual_instance: Optional[Union[Reference, SchemaOneOf]] = None + one_of_schemas: List[str] = Literal["Reference", "SchemaOneOf"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + ModelSchema.model_construct() + error_messages = [] + match = 0 + # validate data type: Reference + if not isinstance(v, Reference): + error_messages.append(f"Error! Input type `{type(v)}` is not `Reference`") + else: + match += 1 + # validate data type: SchemaOneOf + if not isinstance(v, SchemaOneOf): + error_messages.append(f"Error! Input type `{type(v)}` is not `SchemaOneOf`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in ModelSchema with oneOf schemas: Reference, SchemaOneOf. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in ModelSchema with oneOf schemas: Reference, SchemaOneOf. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into Reference + try: + instance.actual_instance = Reference.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into SchemaOneOf + try: + instance.actual_instance = SchemaOneOf.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into ModelSchema with oneOf schemas: Reference, SchemaOneOf. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into ModelSchema with oneOf schemas: Reference, SchemaOneOf. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg.py b/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg.py new file mode 100644 index 0000000..5a5f97f --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg.py @@ -0,0 +1,113 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field + +from unity_sps_ogc_processes_api.models.ogcapppkg_execution_unit import ( + OgcapppkgExecutionUnit, +) +from unity_sps_ogc_processes_api.models.process import Process + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Ogcapppkg(BaseModel): + """ + Ogcapppkg + """ # noqa: E501 + + process_description: Optional[Process] = Field( + default=None, alias="processDescription" + ) + execution_unit: OgcapppkgExecutionUnit = Field(alias="executionUnit") + __properties: ClassVar[List[str]] = ["processDescription", "executionUnit"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of Ogcapppkg from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of process_description + if self.process_description: + _dict["processDescription"] = self.process_description.to_dict() + # override the default output from pydantic by calling `to_dict()` of execution_unit + if self.execution_unit: + _dict["executionUnit"] = self.execution_unit.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of Ogcapppkg from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "processDescription": ( + Process.from_dict(obj.get("processDescription")) + if obj.get("processDescription") is not None + else None + ), + "executionUnit": ( + OgcapppkgExecutionUnit.from_dict(obj.get("executionUnit")) + if obj.get("executionUnit") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py b/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py new file mode 100644 index 0000000..eea9993 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py @@ -0,0 +1,180 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.execution_unit import ExecutionUnit +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.qualified_input_value import QualifiedInputValue + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +OGCAPPPKGARRAYINNER_ONE_OF_SCHEMAS = ["ExecutionUnit", "Link", "QualifiedInputValue"] + + +class OgcapppkgArrayInner(BaseModel): + """ + OgcapppkgArrayInner + """ + + # data type: ExecutionUnit + oneof_schema_1_validator: Optional[ExecutionUnit] = None + # data type: Link + oneof_schema_2_validator: Optional[Link] = None + # data type: QualifiedInputValue + oneof_schema_3_validator: Optional[QualifiedInputValue] = None + actual_instance: Optional[Union[ExecutionUnit, Link, QualifiedInputValue]] = None + one_of_schemas: List[str] = Literal["ExecutionUnit", "Link", "QualifiedInputValue"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + OgcapppkgArrayInner.model_construct() + error_messages = [] + match = 0 + # validate data type: ExecutionUnit + if not isinstance(v, ExecutionUnit): + error_messages.append( + f"Error! Input type `{type(v)}` is not `ExecutionUnit`" + ) + else: + match += 1 + # validate data type: Link + if not isinstance(v, Link): + error_messages.append(f"Error! Input type `{type(v)}` is not `Link`") + else: + match += 1 + # validate data type: QualifiedInputValue + if not isinstance(v, QualifiedInputValue): + error_messages.append( + f"Error! Input type `{type(v)}` is not `QualifiedInputValue`" + ) + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in OgcapppkgArrayInner with oneOf schemas: ExecutionUnit, Link, QualifiedInputValue. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in OgcapppkgArrayInner with oneOf schemas: ExecutionUnit, Link, QualifiedInputValue. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into ExecutionUnit + try: + instance.actual_instance = ExecutionUnit.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into Link + try: + instance.actual_instance = Link.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into QualifiedInputValue + try: + instance.actual_instance = QualifiedInputValue.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into OgcapppkgArrayInner with oneOf schemas: ExecutionUnit, Link, QualifiedInputValue. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into OgcapppkgArrayInner with oneOf schemas: ExecutionUnit, Link, QualifiedInputValue. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py b/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py new file mode 100644 index 0000000..2aca797 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py @@ -0,0 +1,207 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.execution_unit import ExecutionUnit +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.ogcapppkg_array_inner import OgcapppkgArrayInner +from unity_sps_ogc_processes_api.models.qualified_input_value import QualifiedInputValue + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +OGCAPPPKGEXECUTIONUNIT_ONE_OF_SCHEMAS = [ + "ExecutionUnit", + "Link", + "List[OgcapppkgArrayInner]", + "QualifiedInputValue", +] + + +class OgcapppkgExecutionUnit(BaseModel): + """ + OgcapppkgExecutionUnit + """ + + # data type: ExecutionUnit + oneof_schema_1_validator: Optional[ExecutionUnit] = None + # data type: Link + oneof_schema_2_validator: Optional[Link] = None + # data type: QualifiedInputValue + oneof_schema_3_validator: Optional[QualifiedInputValue] = None + # data type: List[OgcapppkgArrayInner] + oneof_schema_4_validator: Optional[List[OgcapppkgArrayInner]] = None + actual_instance: Optional[ + Union[ExecutionUnit, Link, List[OgcapppkgArrayInner], QualifiedInputValue] + ] = None + one_of_schemas: List[str] = Literal[ + "ExecutionUnit", "Link", "List[OgcapppkgArrayInner]", "QualifiedInputValue" + ] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = OgcapppkgExecutionUnit.model_construct() + error_messages = [] + match = 0 + # validate data type: ExecutionUnit + if not isinstance(v, ExecutionUnit): + error_messages.append( + f"Error! Input type `{type(v)}` is not `ExecutionUnit`" + ) + else: + match += 1 + # validate data type: Link + if not isinstance(v, Link): + error_messages.append(f"Error! Input type `{type(v)}` is not `Link`") + else: + match += 1 + # validate data type: QualifiedInputValue + if not isinstance(v, QualifiedInputValue): + error_messages.append( + f"Error! Input type `{type(v)}` is not `QualifiedInputValue`" + ) + else: + match += 1 + # validate data type: List[OgcapppkgArrayInner] + try: + instance.oneof_schema_4_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in OgcapppkgExecutionUnit with oneOf schemas: ExecutionUnit, Link, List[OgcapppkgArrayInner], QualifiedInputValue. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in OgcapppkgExecutionUnit with oneOf schemas: ExecutionUnit, Link, List[OgcapppkgArrayInner], QualifiedInputValue. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into ExecutionUnit + try: + instance.actual_instance = ExecutionUnit.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into Link + try: + instance.actual_instance = Link.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into QualifiedInputValue + try: + instance.actual_instance = QualifiedInputValue.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into List[OgcapppkgArrayInner] + try: + # validation + instance.oneof_schema_4_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_4_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into OgcapppkgExecutionUnit with oneOf schemas: ExecutionUnit, Link, List[OgcapppkgArrayInner], QualifiedInputValue. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into OgcapppkgExecutionUnit with oneOf schemas: ExecutionUnit, Link, List[OgcapppkgArrayInner], QualifiedInputValue. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/output.py b/app-cp/src/unity_sps_ogc_processes_api/models/output.py new file mode 100644 index 0000000..bfc5305 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/output.py @@ -0,0 +1,99 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel + +from unity_sps_ogc_processes_api.models.format import Format + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Output(BaseModel): + """ + Output + """ # noqa: E501 + + format: Optional[Format] = None + __properties: ClassVar[List[str]] = ["format"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of Output from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of format + if self.format: + _dict["format"] = self.format.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of Output from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "format": ( + Format.from_dict(obj.get("format")) + if obj.get("format") is not None + else None + ) + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/output_description.py b/app-cp/src/unity_sps_ogc_processes_api/models/output_description.py new file mode 100644 index 0000000..7ea41d5 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/output_description.py @@ -0,0 +1,125 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.metadata import Metadata +from unity_sps_ogc_processes_api.models.model_schema import ModelSchema + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class OutputDescription(BaseModel): + """ + OutputDescription + """ # noqa: E501 + + title: Optional[StrictStr] = None + description: Optional[StrictStr] = None + keywords: Optional[List[StrictStr]] = None + metadata: Optional[List[Metadata]] = None + schema: ModelSchema = Field(alias="schema") + __properties: ClassVar[List[str]] = [ + "title", + "description", + "keywords", + "metadata", + "schema", + ] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of OutputDescription from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in metadata (list) + _items = [] + if self.metadata: + for _item in self.metadata: + if _item: + _items.append(_item.to_dict()) + _dict["metadata"] = _items + # override the default output from pydantic by calling `to_dict()` of schema + if self.schema: + _dict["schema"] = self.schema.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of OutputDescription from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "title": obj.get("title"), + "description": obj.get("description"), + "keywords": obj.get("keywords"), + "metadata": ( + [Metadata.from_dict(_item) for _item in obj.get("metadata")] + if obj.get("metadata") is not None + else None + ), + "schema": ( + ModelSchema.from_dict(obj.get("schema")) + if obj.get("schema") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/output_workflows.py b/app-cp/src/unity_sps_ogc_processes_api/models/output_workflows.py new file mode 100644 index 0000000..7431654 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/output_workflows.py @@ -0,0 +1,101 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.format import Format + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class OutputWorkflows(BaseModel): + """ + OutputWorkflows + """ # noqa: E501 + + format: Optional[Format] = None + output: Optional[StrictStr] = Field(default=None, alias="$output") + __properties: ClassVar[List[str]] = ["format", "$output"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of OutputWorkflows from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of format + if self.format: + _dict["format"] = self.format.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of OutputWorkflows from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "format": ( + Format.from_dict(obj.get("format")) + if obj.get("format") is not None + else None + ), + "$output": obj.get("$output"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/output_workflows1.py b/app-cp/src/unity_sps_ogc_processes_api/models/output_workflows1.py new file mode 100644 index 0000000..831e686 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/output_workflows1.py @@ -0,0 +1,101 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.format import Format + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class OutputWorkflows1(BaseModel): + """ + OutputWorkflows1 + """ # noqa: E501 + + format: Optional[Format] = None + output: Optional[StrictStr] = Field(default=None, alias="$output") + __properties: ClassVar[List[str]] = ["format", "$output"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of OutputWorkflows1 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of format + if self.format: + _dict["format"] = self.format.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of OutputWorkflows1 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "format": ( + Format.from_dict(obj.get("format")) + if obj.get("format") is not None + else None + ), + "$output": obj.get("$output"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/process.py b/app-cp/src/unity_sps_ogc_processes_api/models/process.py new file mode 100644 index 0000000..af787f9 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/process.py @@ -0,0 +1,177 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.input_description import InputDescription +from unity_sps_ogc_processes_api.models.job_control_options import JobControlOptions +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.metadata import Metadata +from unity_sps_ogc_processes_api.models.output_description import OutputDescription + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Process(BaseModel): + """ + Process + """ # noqa: E501 + + title: Optional[StrictStr] = None + description: Optional[StrictStr] = None + keywords: Optional[List[StrictStr]] = None + metadata: Optional[List[Metadata]] = None + id: StrictStr + version: StrictStr + job_control_options: Optional[List[JobControlOptions]] = Field( + default=None, alias="jobControlOptions" + ) + links: Optional[List[Link]] = None + inputs: Optional[Dict[str, InputDescription]] = None + outputs: Optional[Dict[str, OutputDescription]] = None + __properties: ClassVar[List[str]] = [ + "title", + "description", + "keywords", + "metadata", + "id", + "version", + "jobControlOptions", + "links", + "inputs", + "outputs", + ] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of Process from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in metadata (list) + _items = [] + if self.metadata: + for _item in self.metadata: + if _item: + _items.append(_item.to_dict()) + _dict["metadata"] = _items + # override the default output from pydantic by calling `to_dict()` of each item in links (list) + _items = [] + if self.links: + for _item in self.links: + if _item: + _items.append(_item.to_dict()) + _dict["links"] = _items + # override the default output from pydantic by calling `to_dict()` of each value in inputs (dict) + _field_dict = {} + if self.inputs: + for _key in self.inputs: + if self.inputs[_key]: + _field_dict[_key] = self.inputs[_key].to_dict() + _dict["inputs"] = _field_dict + # override the default output from pydantic by calling `to_dict()` of each value in outputs (dict) + _field_dict = {} + if self.outputs: + for _key in self.outputs: + if self.outputs[_key]: + _field_dict[_key] = self.outputs[_key].to_dict() + _dict["outputs"] = _field_dict + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of Process from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "title": obj.get("title"), + "description": obj.get("description"), + "keywords": obj.get("keywords"), + "metadata": ( + [Metadata.from_dict(_item) for _item in obj.get("metadata")] + if obj.get("metadata") is not None + else None + ), + "id": obj.get("id"), + "version": obj.get("version"), + "jobControlOptions": obj.get("jobControlOptions"), + "links": ( + [Link.from_dict(_item) for _item in obj.get("links")] + if obj.get("links") is not None + else None + ), + "inputs": ( + dict( + (_k, InputDescription.from_dict(_v)) + for _k, _v in obj.get("inputs").items() + ) + if obj.get("inputs") is not None + else None + ), + "outputs": ( + dict( + (_k, OutputDescription.from_dict(_v)) + for _k, _v in obj.get("outputs").items() + ) + if obj.get("outputs") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/process_list.py b/app-cp/src/unity_sps_ogc_processes_api/models/process_list.py new file mode 100644 index 0000000..d7ac593 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/process_list.py @@ -0,0 +1,117 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List + +from pydantic import BaseModel + +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.process_summary import ProcessSummary + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class ProcessList(BaseModel): + """ + ProcessList + """ # noqa: E501 + + processes: List[ProcessSummary] + links: List[Link] + __properties: ClassVar[List[str]] = ["processes", "links"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ProcessList from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in processes (list) + _items = [] + if self.processes: + for _item in self.processes: + if _item: + _items.append(_item.to_dict()) + _dict["processes"] = _items + # override the default output from pydantic by calling `to_dict()` of each item in links (list) + _items = [] + if self.links: + for _item in self.links: + if _item: + _items.append(_item.to_dict()) + _dict["links"] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of ProcessList from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "processes": ( + [ProcessSummary.from_dict(_item) for _item in obj.get("processes")] + if obj.get("processes") is not None + else None + ), + "links": ( + [Link.from_dict(_item) for _item in obj.get("links")] + if obj.get("links") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/process_summary.py b/app-cp/src/unity_sps_ogc_processes_api/models/process_summary.py new file mode 100644 index 0000000..d68436a --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/process_summary.py @@ -0,0 +1,141 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.job_control_options import JobControlOptions +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.metadata import Metadata + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class ProcessSummary(BaseModel): + """ + ProcessSummary + """ # noqa: E501 + + title: Optional[StrictStr] = None + description: Optional[StrictStr] = None + keywords: Optional[List[StrictStr]] = None + metadata: Optional[List[Metadata]] = None + id: StrictStr + version: StrictStr + job_control_options: Optional[List[JobControlOptions]] = Field( + default=None, alias="jobControlOptions" + ) + links: Optional[List[Link]] = None + __properties: ClassVar[List[str]] = [ + "title", + "description", + "keywords", + "metadata", + "id", + "version", + "jobControlOptions", + "links", + ] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ProcessSummary from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in metadata (list) + _items = [] + if self.metadata: + for _item in self.metadata: + if _item: + _items.append(_item.to_dict()) + _dict["metadata"] = _items + # override the default output from pydantic by calling `to_dict()` of each item in links (list) + _items = [] + if self.links: + for _item in self.links: + if _item: + _items.append(_item.to_dict()) + _dict["links"] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of ProcessSummary from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "title": obj.get("title"), + "description": obj.get("description"), + "keywords": obj.get("keywords"), + "metadata": ( + [Metadata.from_dict(_item) for _item in obj.get("metadata")] + if obj.get("metadata") is not None + else None + ), + "id": obj.get("id"), + "version": obj.get("version"), + "jobControlOptions": obj.get("jobControlOptions"), + "links": ( + [Link.from_dict(_item) for _item in obj.get("links")] + if obj.get("links") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/processes_list.py b/app-cp/src/unity_sps_ogc_processes_api/models/processes_list.py new file mode 100644 index 0000000..f010719 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/processes_list.py @@ -0,0 +1,43 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import re # noqa: F401 +from enum import Enum + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class ProcessesList(str, Enum): + """ + ProcessesList + """ + + """ + allowed enum values + """ + RENDERMAP = "RenderMap" + ELEVATIONCONTOURS = "ElevationContours" + OSMERE = "OSMERE" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of ProcessesList from a JSON string""" + return cls(json.loads(json_str)) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value.py b/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value.py new file mode 100644 index 0000000..83abf4b --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value.py @@ -0,0 +1,113 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.format_schema import FormatSchema +from unity_sps_ogc_processes_api.models.input_value import InputValue + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class QualifiedInputValue(BaseModel): + """ + QualifiedInputValue + """ # noqa: E501 + + media_type: Optional[StrictStr] = Field(default=None, alias="mediaType") + encoding: Optional[StrictStr] = None + schema: Optional[FormatSchema] = Field(default=None, alias="schema") + value: InputValue + __properties: ClassVar[List[str]] = ["mediaType", "encoding", "schema", "value"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of QualifiedInputValue from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of schema + if self.schema: + _dict["schema"] = self.schema.to_dict() + # override the default output from pydantic by calling `to_dict()` of value + if self.value: + _dict["value"] = self.value.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of QualifiedInputValue from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "mediaType": obj.get("mediaType"), + "encoding": obj.get("encoding"), + "schema": ( + FormatSchema.from_dict(obj.get("schema")) + if obj.get("schema") is not None + else None + ), + "value": ( + InputValue.from_dict(obj.get("value")) + if obj.get("value") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py b/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py new file mode 100644 index 0000000..a7544fc --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py @@ -0,0 +1,113 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.format_schema import FormatSchema +from unity_sps_ogc_processes_api.models.input_value1 import InputValue1 + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class QualifiedInputValue1(BaseModel): + """ + QualifiedInputValue1 + """ # noqa: E501 + + media_type: Optional[StrictStr] = Field(default=None, alias="mediaType") + encoding: Optional[StrictStr] = None + schema: Optional[FormatSchema] = Field(default=None, alias="schema") + value: InputValue1 + __properties: ClassVar[List[str]] = ["mediaType", "encoding", "schema", "value"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of QualifiedInputValue1 from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of schema + if self.schema: + _dict["schema"] = self.schema.to_dict() + # override the default output from pydantic by calling `to_dict()` of value + if self.value: + _dict["value"] = self.value.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of QualifiedInputValue1 from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "mediaType": obj.get("mediaType"), + "encoding": obj.get("encoding"), + "schema": ( + FormatSchema.from_dict(obj.get("schema")) + if obj.get("schema") is not None + else None + ), + "value": ( + InputValue1.from_dict(obj.get("value")) + if obj.get("value") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py b/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py new file mode 100644 index 0000000..0336727 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py @@ -0,0 +1,142 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( + FieldsModifiersProperties, +) +from unity_sps_ogc_processes_api.models.format_schema import FormatSchema + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class QualifiedInputValueWorkflows(BaseModel): + """ + QualifiedInputValueWorkflows + """ # noqa: E501 + + media_type: Optional[StrictStr] = Field(default=None, alias="mediaType") + encoding: Optional[StrictStr] = None + schema: Optional[FormatSchema] = Field(default=None, alias="schema") + filter: Optional[StrictStr] = None + properties: Optional[FieldsModifiersProperties] = None + sort_by: Optional[List[StrictStr]] = Field(default=None, alias="sortBy") + value: InputValueWorkflows + __properties: ClassVar[List[str]] = [ + "mediaType", + "encoding", + "schema", + "filter", + "properties", + "sortBy", + "value", + ] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of QualifiedInputValueWorkflows from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of schema + if self.schema: + _dict["schema"] = self.schema.to_dict() + # override the default output from pydantic by calling `to_dict()` of properties + if self.properties: + _dict["properties"] = self.properties.to_dict() + # override the default output from pydantic by calling `to_dict()` of value + if self.value: + _dict["value"] = self.value.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of QualifiedInputValueWorkflows from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "mediaType": obj.get("mediaType"), + "encoding": obj.get("encoding"), + "schema": ( + FormatSchema.from_dict(obj.get("schema")) + if obj.get("schema") is not None + else None + ), + "filter": obj.get("filter"), + "properties": ( + FieldsModifiersProperties.from_dict(obj.get("properties")) + if obj.get("properties") is not None + else None + ), + "sortBy": obj.get("sortBy"), + "value": ( + InputValueWorkflows.from_dict(obj.get("value")) + if obj.get("value") is not None + else None + ), + } + ) + return _obj + + +from unity_sps_ogc_processes_api.models.input_value_workflows import InputValueWorkflows + +# TODO: Rewrite to not use raise_errors +QualifiedInputValueWorkflows.model_rebuild(raise_errors=False) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/reference.py b/app-cp/src/unity_sps_ogc_processes_api/models/reference.py new file mode 100644 index 0000000..7e4745b --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/reference.py @@ -0,0 +1,86 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List + +from pydantic import BaseModel, Field, StrictStr + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Reference(BaseModel): + """ + Reference + """ # noqa: E501 + + ref: StrictStr = Field(alias="$ref") + __properties: ClassVar[List[str]] = ["$ref"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of Reference from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of Reference from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({"$ref": obj.get("$ref")}) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/schema1.py b/app-cp/src/unity_sps_ogc_processes_api/models/schema1.py new file mode 100644 index 0000000..b31f8f1 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/schema1.py @@ -0,0 +1,167 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, ValidationError, field_validator +from typing_extensions import Literal + +from unity_sps_ogc_processes_api.models.reference import Reference + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +SCHEMA1_ONE_OF_SCHEMAS = ["Reference", "SchemaOneOf"] + + +class Schema1(BaseModel): + """ + Schema1 + """ + + # data type: Reference + oneof_schema_1_validator: Optional[Reference] = None + # data type: SchemaOneOf + oneof_schema_2_validator: Optional[SchemaOneOf] = None + actual_instance: Optional[Union[Reference, SchemaOneOf]] = None + one_of_schemas: List[str] = Literal["Reference", "SchemaOneOf"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + Schema1.model_construct() + error_messages = [] + match = 0 + # validate data type: Reference + if not isinstance(v, Reference): + error_messages.append(f"Error! Input type `{type(v)}` is not `Reference`") + else: + match += 1 + # validate data type: SchemaOneOf + if not isinstance(v, SchemaOneOf): + error_messages.append(f"Error! Input type `{type(v)}` is not `SchemaOneOf`") + else: + match += 1 + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in Schema1 with oneOf schemas: Reference, SchemaOneOf. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in Schema1 with oneOf schemas: Reference, SchemaOneOf. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into Reference + try: + instance.actual_instance = Reference.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into SchemaOneOf + try: + instance.actual_instance = SchemaOneOf.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into Schema1 with oneOf schemas: Reference, SchemaOneOf. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into Schema1 with oneOf schemas: Reference, SchemaOneOf. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + +from unity_sps_ogc_processes_api.models.schema_one_of import SchemaOneOf + +# TODO: Rewrite to not use raise_errors +Schema1.model_rebuild(raise_errors=False) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/schema_one_of.py b/app-cp/src/unity_sps_ogc_processes_api/models/schema_one_of.py new file mode 100644 index 0000000..7ded79c --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/schema_one_of.py @@ -0,0 +1,349 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional, Union + +from pydantic import ( + BaseModel, + Field, + StrictBool, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) +from typing_extensions import Annotated + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class SchemaOneOf(BaseModel): + """ + SchemaOneOf + """ # noqa: E501 + + title: Optional[StrictStr] = None + multiple_of: Optional[ + Union[ + Annotated[float, Field(strict=True, gt=0)], + Annotated[int, Field(strict=True, gt=0)], + ] + ] = Field(default=None, alias="multipleOf") + maximum: Optional[Union[StrictFloat, StrictInt]] = None + exclusive_maximum: Optional[StrictBool] = Field( + default=False, alias="exclusiveMaximum" + ) + minimum: Optional[Union[StrictFloat, StrictInt]] = None + exclusive_minimum: Optional[StrictBool] = Field( + default=False, alias="exclusiveMinimum" + ) + max_length: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( + default=None, alias="maxLength" + ) + min_length: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( + default=0, alias="minLength" + ) + pattern: Optional[StrictStr] = None + max_items: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( + default=None, alias="maxItems" + ) + min_items: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( + default=0, alias="minItems" + ) + unique_items: Optional[StrictBool] = Field(default=False, alias="uniqueItems") + max_properties: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( + default=None, alias="maxProperties" + ) + min_properties: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( + default=0, alias="minProperties" + ) + required: Optional[Annotated[List[StrictStr], Field(min_length=1)]] = None + enum: Optional[Annotated[List[Dict[str, Any]], Field(min_length=1)]] = None + type: Optional[StrictStr] = None + is_not: Optional[Schema1] = Field(default=None, alias="not") + all_of: Optional[List[Schema1]] = Field(default=None, alias="allOf") + one_of: Optional[List[Schema1]] = Field(default=None, alias="oneOf") + any_of: Optional[List[Schema1]] = Field(default=None, alias="anyOf") + items: Optional[Schema1] = None + properties: Optional[Dict[str, Schema1]] = None + additional_properties: Optional[SchemaOneOfAdditionalProperties] = Field( + default=None, alias="additionalProperties" + ) + description: Optional[StrictStr] = None + format: Optional[StrictStr] = None + default: Optional[Dict[str, Any]] = None + nullable: Optional[StrictBool] = False + read_only: Optional[StrictBool] = Field(default=False, alias="readOnly") + write_only: Optional[StrictBool] = Field(default=False, alias="writeOnly") + example: Optional[Dict[str, Any]] = None + deprecated: Optional[StrictBool] = False + content_media_type: Optional[StrictStr] = Field( + default=None, alias="contentMediaType" + ) + content_encoding: Optional[StrictStr] = Field(default=None, alias="contentEncoding") + content_schema: Optional[StrictStr] = Field(default=None, alias="contentSchema") + __properties: ClassVar[List[str]] = [ + "title", + "multipleOf", + "maximum", + "exclusiveMaximum", + "minimum", + "exclusiveMinimum", + "maxLength", + "minLength", + "pattern", + "maxItems", + "minItems", + "uniqueItems", + "maxProperties", + "minProperties", + "required", + "enum", + "type", + "not", + "allOf", + "oneOf", + "anyOf", + "items", + "properties", + "additionalProperties", + "description", + "format", + "default", + "nullable", + "readOnly", + "writeOnly", + "example", + "deprecated", + "contentMediaType", + "contentEncoding", + "contentSchema", + ] + + @field_validator("type") + def type_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in ("array", "boolean", "integer", "number", "object", "string"): + raise ValueError( + "must be one of enum values ('array', 'boolean', 'integer', 'number', 'object', 'string')" + ) + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of SchemaOneOf from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of is_not + if self.is_not: + _dict["not"] = self.is_not.to_dict() + # override the default output from pydantic by calling `to_dict()` of each item in all_of (list) + _items = [] + if self.all_of: + for _item in self.all_of: + if _item: + _items.append(_item.to_dict()) + _dict["allOf"] = _items + # override the default output from pydantic by calling `to_dict()` of each item in one_of (list) + _items = [] + if self.one_of: + for _item in self.one_of: + if _item: + _items.append(_item.to_dict()) + _dict["oneOf"] = _items + # override the default output from pydantic by calling `to_dict()` of each item in any_of (list) + _items = [] + if self.any_of: + for _item in self.any_of: + if _item: + _items.append(_item.to_dict()) + _dict["anyOf"] = _items + # override the default output from pydantic by calling `to_dict()` of items + if self.items: + _dict["items"] = self.items.to_dict() + # override the default output from pydantic by calling `to_dict()` of each value in properties (dict) + _field_dict = {} + if self.properties: + for _key in self.properties: + if self.properties[_key]: + _field_dict[_key] = self.properties[_key].to_dict() + _dict["properties"] = _field_dict + # override the default output from pydantic by calling `to_dict()` of additional_properties + if self.additional_properties: + _dict["additionalProperties"] = self.additional_properties.to_dict() + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of SchemaOneOf from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "title": obj.get("title"), + "multipleOf": obj.get("multipleOf"), + "maximum": obj.get("maximum"), + "exclusiveMaximum": ( + obj.get("exclusiveMaximum") + if obj.get("exclusiveMaximum") is not None + else False + ), + "minimum": obj.get("minimum"), + "exclusiveMinimum": ( + obj.get("exclusiveMinimum") + if obj.get("exclusiveMinimum") is not None + else False + ), + "maxLength": obj.get("maxLength"), + "minLength": ( + obj.get("minLength") if obj.get("minLength") is not None else 0 + ), + "pattern": obj.get("pattern"), + "maxItems": obj.get("maxItems"), + "minItems": ( + obj.get("minItems") if obj.get("minItems") is not None else 0 + ), + "uniqueItems": ( + obj.get("uniqueItems") + if obj.get("uniqueItems") is not None + else False + ), + "maxProperties": obj.get("maxProperties"), + "minProperties": ( + obj.get("minProperties") + if obj.get("minProperties") is not None + else 0 + ), + "required": obj.get("required"), + "enum": obj.get("enum"), + "type": obj.get("type"), + "not": ( + Schema1.from_dict(obj.get("not")) + if obj.get("not") is not None + else None + ), + "allOf": ( + [Schema1.from_dict(_item) for _item in obj.get("allOf")] + if obj.get("allOf") is not None + else None + ), + "oneOf": ( + [Schema1.from_dict(_item) for _item in obj.get("oneOf")] + if obj.get("oneOf") is not None + else None + ), + "anyOf": ( + [Schema1.from_dict(_item) for _item in obj.get("anyOf")] + if obj.get("anyOf") is not None + else None + ), + "items": ( + Schema1.from_dict(obj.get("items")) + if obj.get("items") is not None + else None + ), + "properties": ( + dict( + (_k, Schema1.from_dict(_v)) + for _k, _v in obj.get("properties").items() + ) + if obj.get("properties") is not None + else None + ), + "additionalProperties": ( + SchemaOneOfAdditionalProperties.from_dict( + obj.get("additionalProperties") + ) + if obj.get("additionalProperties") is not None + else None + ), + "description": obj.get("description"), + "format": obj.get("format"), + "default": obj.get("default"), + "nullable": ( + obj.get("nullable") if obj.get("nullable") is not None else False + ), + "readOnly": ( + obj.get("readOnly") if obj.get("readOnly") is not None else False + ), + "writeOnly": ( + obj.get("writeOnly") if obj.get("writeOnly") is not None else False + ), + "example": obj.get("example"), + "deprecated": ( + obj.get("deprecated") + if obj.get("deprecated") is not None + else False + ), + "contentMediaType": obj.get("contentMediaType"), + "contentEncoding": obj.get("contentEncoding"), + "contentSchema": obj.get("contentSchema"), + } + ) + return _obj + + +from unity_sps_ogc_processes_api.models.schema1 import Schema1 +from unity_sps_ogc_processes_api.models.schema_one_of_additional_properties import ( + SchemaOneOfAdditionalProperties, +) + +# TODO: Rewrite to not use raise_errors +SchemaOneOf.model_rebuild(raise_errors=False) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py b/app-cp/src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py new file mode 100644 index 0000000..19a6381 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py @@ -0,0 +1,169 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel, StrictBool, ValidationError, field_validator +from typing_extensions import Literal + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +SCHEMAONEOFADDITIONALPROPERTIES_ONE_OF_SCHEMAS = ["Schema1", "bool"] + + +class SchemaOneOfAdditionalProperties(BaseModel): + """ + SchemaOneOfAdditionalProperties + """ + + # data type: Schema1 + oneof_schema_1_validator: Optional[Schema1] = None + # data type: bool + oneof_schema_2_validator: Optional[StrictBool] = None + actual_instance: Optional[Union[Schema1, bool]] = None + one_of_schemas: List[str] = Literal["Schema1", "bool"] + + model_config = { + "validate_assignment": True, + "protected_namespaces": (), + } + + def __init__(self, *args, **kwargs) -> None: + if args: + if len(args) > 1: + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) + if kwargs: + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) + super().__init__(actual_instance=args[0]) + else: + super().__init__(**kwargs) + + @field_validator("actual_instance") + def actual_instance_must_validate_oneof(cls, v): + instance = SchemaOneOfAdditionalProperties.model_construct() + error_messages = [] + match = 0 + # validate data type: Schema1 + if not isinstance(v, Schema1): + error_messages.append(f"Error! Input type `{type(v)}` is not `Schema1`") + else: + match += 1 + # validate data type: bool + try: + instance.oneof_schema_2_validator = v + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when setting `actual_instance` in SchemaOneOfAdditionalProperties with oneOf schemas: Schema1, bool. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when setting `actual_instance` in SchemaOneOfAdditionalProperties with oneOf schemas: Schema1, bool. Details: " + + ", ".join(error_messages) + ) + else: + return v + + @classmethod + def from_dict(cls, obj: dict) -> Self: + return cls.from_json(json.dumps(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Returns the object represented by the json string""" + instance = cls.model_construct() + error_messages = [] + match = 0 + + # deserialize data into Schema1 + try: + instance.actual_instance = Schema1.from_json(json_str) + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + # deserialize data into bool + try: + # validation + instance.oneof_schema_2_validator = json.loads(json_str) + # assign value to actual_instance + instance.actual_instance = instance.oneof_schema_2_validator + match += 1 + except (ValidationError, ValueError) as e: + error_messages.append(str(e)) + + if match > 1: + # more than 1 match + raise ValueError( + "Multiple matches found when deserializing the JSON string into SchemaOneOfAdditionalProperties with oneOf schemas: Schema1, bool. Details: " + + ", ".join(error_messages) + ) + elif match == 0: + # no match + raise ValueError( + "No match found when deserializing the JSON string into SchemaOneOfAdditionalProperties with oneOf schemas: Schema1, bool. Details: " + + ", ".join(error_messages) + ) + else: + return instance + + def to_json(self) -> str: + """Returns the JSON representation of the actual instance""" + if self.actual_instance is None: + return "null" + + to_json = getattr(self.actual_instance, "to_json", None) + if callable(to_json): + return self.actual_instance.to_json() + else: + return json.dumps(self.actual_instance) + + def to_dict(self) -> Dict: + """Returns the dict representation of the actual instance""" + if self.actual_instance is None: + return None + + to_dict = getattr(self.actual_instance, "to_dict", None) + if callable(to_dict): + return self.actual_instance.to_dict() + else: + # primitive type + return self.actual_instance + + def to_str(self) -> str: + """Returns the string representation of the actual instance""" + return pprint.pformat(self.model_dump()) + + +from unity_sps_ogc_processes_api.models.schema1 import Schema1 + +# TODO: Rewrite to not use raise_errors +SchemaOneOfAdditionalProperties.model_rebuild(raise_errors=False) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/static_indicator.py b/app-cp/src/unity_sps_ogc_processes_api/models/static_indicator.py new file mode 100644 index 0000000..24e3b14 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/static_indicator.py @@ -0,0 +1,146 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictBool, StrictStr + +from unity_sps_ogc_processes_api.models.job_control_options import JobControlOptions +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.metadata import Metadata + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class StaticIndicator(BaseModel): + """ + StaticIndicator + """ # noqa: E501 + + title: Optional[StrictStr] = None + description: Optional[StrictStr] = None + keywords: Optional[List[StrictStr]] = None + metadata: Optional[List[Metadata]] = None + id: StrictStr + version: StrictStr + job_control_options: Optional[List[JobControlOptions]] = Field( + default=None, alias="jobControlOptions" + ) + links: Optional[List[Link]] = None + mutable: Optional[StrictBool] = True + __properties: ClassVar[List[str]] = [ + "title", + "description", + "keywords", + "metadata", + "id", + "version", + "jobControlOptions", + "links", + "mutable", + ] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of StaticIndicator from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of each item in metadata (list) + _items = [] + if self.metadata: + for _item in self.metadata: + if _item: + _items.append(_item.to_dict()) + _dict["metadata"] = _items + # override the default output from pydantic by calling `to_dict()` of each item in links (list) + _items = [] + if self.links: + for _item in self.links: + if _item: + _items.append(_item.to_dict()) + _dict["links"] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of StaticIndicator from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "title": obj.get("title"), + "description": obj.get("description"), + "keywords": obj.get("keywords"), + "metadata": ( + [Metadata.from_dict(_item) for _item in obj.get("metadata")] + if obj.get("metadata") is not None + else None + ), + "id": obj.get("id"), + "version": obj.get("version"), + "jobControlOptions": obj.get("jobControlOptions"), + "links": ( + [Link.from_dict(_item) for _item in obj.get("links")] + if obj.get("links") is not None + else None + ), + "mutable": ( + obj.get("mutable") if obj.get("mutable") is not None else True + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/status_code.py b/app-cp/src/unity_sps_ogc_processes_api/models/status_code.py new file mode 100644 index 0000000..2a873fa --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/status_code.py @@ -0,0 +1,45 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import re # noqa: F401 +from enum import Enum + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class StatusCode(str, Enum): + """ + StatusCode + """ + + """ + allowed enum values + """ + ACCEPTED = "accepted" + RUNNING = "running" + SUCCESSFUL = "successful" + FAILED = "failed" + DISMISSED = "dismissed" + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of StatusCode from a JSON string""" + return cls(json.loads(json_str)) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/status_info.py b/app-cp/src/unity_sps_ogc_processes_api/models/status_info.py new file mode 100644 index 0000000..212d05c --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/status_info.py @@ -0,0 +1,156 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from datetime import datetime +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr, field_validator +from typing_extensions import Annotated + +from unity_sps_ogc_processes_api.models.exception import Exception +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.status_code import StatusCode + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class StatusInfo(BaseModel): + """ + StatusInfo + """ # noqa: E501 + + process_id: Optional[StrictStr] = Field(default=None, alias="processID") + type: StrictStr + job_id: StrictStr = Field(alias="jobID") + status: StatusCode + message: Optional[StrictStr] = None + exception: Optional[Exception] = None + created: Optional[datetime] = None + started: Optional[datetime] = None + finished: Optional[datetime] = None + updated: Optional[datetime] = None + progress: Optional[Annotated[int, Field(le=100, strict=True, ge=0)]] = None + links: Optional[List[Link]] = None + __properties: ClassVar[List[str]] = [ + "processID", + "type", + "jobID", + "status", + "message", + "exception", + "created", + "started", + "finished", + "updated", + "progress", + "links", + ] + + @field_validator("type") + def type_validate_enum(cls, value): + """Validates the enum""" + if value not in ("process"): + raise ValueError("must be one of enum values ('process')") + return value + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of StatusInfo from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + # override the default output from pydantic by calling `to_dict()` of exception + if self.exception: + _dict["exception"] = self.exception.to_dict() + # override the default output from pydantic by calling `to_dict()` of each item in links (list) + _items = [] + if self.links: + for _item in self.links: + if _item: + _items.append(_item.to_dict()) + _dict["links"] = _items + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of StatusInfo from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "processID": obj.get("processID"), + "type": obj.get("type"), + "jobID": obj.get("jobID"), + "status": obj.get("status"), + "message": obj.get("message"), + "exception": ( + Exception.from_dict(obj.get("exception")) + if obj.get("exception") is not None + else None + ), + "created": obj.get("created"), + "started": obj.get("started"), + "finished": obj.get("finished"), + "updated": obj.get("updated"), + "progress": obj.get("progress"), + "links": ( + [Link.from_dict(_item) for _item in obj.get("links")] + if obj.get("links") is not None + else None + ), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/subscriber.py b/app-cp/src/unity_sps_ogc_processes_api/models/subscriber.py new file mode 100644 index 0000000..d67d0a0 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/models/subscriber.py @@ -0,0 +1,94 @@ +# coding: utf-8 + +""" + OGC API - Processes + + Example API Definition for OGC API - Processes + + The version of the OpenAPI document: 0.1 + Contact: info@ogc.org + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations + +import json +import pprint +import re # noqa: F401 +from typing import Any, ClassVar, Dict, List, Optional + +from pydantic import BaseModel, Field, StrictStr + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Subscriber(BaseModel): + """ + Optional URIs for callbacks for this job. Support for this parameter is not required and the parameter may be removed from the API definition, if conformance class **'callback'** is not listed in the conformance declaration under `/conformance`. + """ # noqa: E501 + + success_uri: StrictStr = Field(alias="successUri") + in_progress_uri: Optional[StrictStr] = Field(default=None, alias="inProgressUri") + failed_uri: Optional[StrictStr] = Field(default=None, alias="failedUri") + __properties: ClassVar[List[str]] = ["successUri", "inProgressUri", "failedUri"] + + model_config = { + "populate_by_name": True, + "validate_assignment": True, + "protected_namespaces": (), + } + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Self: + """Create an instance of Subscriber from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + _dict = self.model_dump( + by_alias=True, + exclude={}, + exclude_none=True, + ) + return _dict + + @classmethod + def from_dict(cls, obj: Dict) -> Self: + """Create an instance of Subscriber from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate( + { + "successUri": obj.get("successUri"), + "inProgressUri": obj.get("inProgressUri"), + "failedUri": obj.get("failedUri"), + } + ) + return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/security_api.py b/app-cp/src/unity_sps_ogc_processes_api/security_api.py new file mode 100644 index 0000000..dfc6b32 --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/security_api.py @@ -0,0 +1,20 @@ +# coding: utf-8 + + +from fastapi import Depends, Security # noqa: F401 +from fastapi.openapi.models import OAuthFlowImplicit, OAuthFlows # noqa: F401 +from fastapi.security import ( # noqa: F401 + HTTPAuthorizationCredentials, + HTTPBasic, + HTTPBasicCredentials, + HTTPBearer, + OAuth2, + OAuth2AuthorizationCodeBearer, + OAuth2PasswordBearer, + SecurityScopes, +) +from fastapi.security.api_key import ( # noqa: F401 + APIKeyCookie, + APIKeyHeader, + APIKeyQuery, +) diff --git a/app-cp/tests/conftest.py b/app-cp/tests/conftest.py new file mode 100644 index 0000000..a881b4f --- /dev/null +++ b/app-cp/tests/conftest.py @@ -0,0 +1,17 @@ +import pytest +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from unity_sps_ogc_processes_api.main import app as application + + +@pytest.fixture +def app() -> FastAPI: + application.dependency_overrides = {} + + return application + + +@pytest.fixture +def client(app) -> TestClient: + return TestClient(app) diff --git a/app-cp/tests/test_api_api.py b/app-cp/tests/test_api_api.py new file mode 100644 index 0000000..8e29116 --- /dev/null +++ b/app-cp/tests/test_api_api.py @@ -0,0 +1,40 @@ +# coding: utf-8 + +from fastapi.testclient import TestClient + +from unity_sps_ogc_processes_api.models.enumeration import Enumeration # noqa: F401 +from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 + + +def test_get_api(client: TestClient): + """Test case for get_api + + Retrieve this API definition. + """ + # uncomment below to make a request + # response = client.request( + # "GET", + # "/api", + # headers=headers, + # params=params, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 + + +def test_get_api_processes(client: TestClient): + """Test case for get_api_processes + + Retrieve the list of processes available from this API implementation & deployment. + """ + # uncomment below to make a request + # response = client.request( + # "GET", + # "/api/processes-list", + # headers=headers, + # params=params, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 diff --git a/app-cp/tests/test_conformance_api.py b/app-cp/tests/test_conformance_api.py new file mode 100644 index 0000000..99d2372 --- /dev/null +++ b/app-cp/tests/test_conformance_api.py @@ -0,0 +1,23 @@ +# coding: utf-8 + +from fastapi.testclient import TestClient + +from unity_sps_ogc_processes_api.models.conf_classes import ConfClasses # noqa: F401 +from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 + + +def test_get_conformance(client: TestClient): + """Test case for get_conformance + + Retrieve the set of OGC API conformance classes that are supported by this service. + """ + # uncomment below to make a request + # response = client.request( + # "GET", + # "/conformance", + # headers=headers, + # params=params, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 diff --git a/app-cp/tests/test_dru_api.py b/app-cp/tests/test_dru_api.py new file mode 100644 index 0000000..a4cdf42 --- /dev/null +++ b/app-cp/tests/test_dru_api.py @@ -0,0 +1,64 @@ +# coding: utf-8 + +from fastapi.testclient import TestClient + +from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 +from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg # noqa: F401 +from unity_sps_ogc_processes_api.models.processes_list import ( # noqa: F401 + ProcessesList, +) + + +def test_deploy(client: TestClient): + """Test case for deploy + + deploy a process. + """ + Ogcapppkg() + # uncomment below to make a request + # response = client.request( + # "POST", + # "/processes", + # headers=headers, + # json=ogcapppkg, + # params=params, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 + + +def test_replace(client: TestClient): + """Test case for replace + + replace a process. + """ + Ogcapppkg() + + # uncomment below to make a request + # response = client.request( + # "PUT", + # "/processes/{processId}".format(processId=unity_sps_ogc_processes_api.ProcessesList()), + # headers=headers, + # json=ogcapppkg, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 + + +def test_undeploy(client: TestClient): + """Test case for undeploy + + undeploy a process. + """ + + # uncomment below to make a request + # response = client.request( + # "DELETE", + # "/processes/{processId}".format(processId=unity_sps_ogc_processes_api.ProcessesList()), + # headers=headers, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 diff --git a/app-cp/tests/test_jobs_api.py b/app-cp/tests/test_jobs_api.py new file mode 100644 index 0000000..522bedf --- /dev/null +++ b/app-cp/tests/test_jobs_api.py @@ -0,0 +1,78 @@ +# coding: utf-8 + +from fastapi.testclient import TestClient + +from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 +from unity_sps_ogc_processes_api.models.inline_or_ref_data import ( # noqa: F401 + InlineOrRefData, +) +from unity_sps_ogc_processes_api.models.job_list import JobList # noqa: F401 +from unity_sps_ogc_processes_api.models.status_info import StatusInfo # noqa: F401 + + +def test_dismiss(client: TestClient): + """Test case for dismiss + + cancel a job execution, remove a finished job + """ + + # uncomment below to make a request + # response = client.request( + # "DELETE", + # "/jobs/{jobId}".format(jobId='job_id_example'), + # headers=headers, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 + + +def test_get_jobs(client: TestClient): + """Test case for get_jobs + + retrieve the list of jobs. + """ + + # uncomment below to make a request + # response = client.request( + # "GET", + # "/jobs", + # headers=headers, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 + + +def test_get_result(client: TestClient): + """Test case for get_result + + retrieve the result(s) of a job + """ + + # uncomment below to make a request + # response = client.request( + # "GET", + # "/jobs/{jobId}/results".format(jobId='job_id_example'), + # headers=headers, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 + + +def test_get_status(client: TestClient): + """Test case for get_status + + retrieve the status of a job + """ + + # uncomment below to make a request + # response = client.request( + # "GET", + # "/jobs/{jobId}".format(jobId='job_id_example'), + # headers=headers, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 diff --git a/app-cp/tests/test_landing_page_api.py b/app-cp/tests/test_landing_page_api.py new file mode 100644 index 0000000..d092aee --- /dev/null +++ b/app-cp/tests/test_landing_page_api.py @@ -0,0 +1,23 @@ +# coding: utf-8 + +from fastapi.testclient import TestClient + +from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 +from unity_sps_ogc_processes_api.models.landing_page import LandingPage # noqa: F401 + + +def test_get_landing_page(client: TestClient): + """Test case for get_landing_page + + Retrieve the OGC API landing page for this service. + """ + # uncomment below to make a request + # response = client.request( + # "GET", + # "/", + # headers=headers, + # params=params, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 diff --git a/app-cp/tests/test_processes_api.py b/app-cp/tests/test_processes_api.py new file mode 100644 index 0000000..914bdd0 --- /dev/null +++ b/app-cp/tests/test_processes_api.py @@ -0,0 +1,73 @@ +# coding: utf-8 + +from fastapi.testclient import TestClient + +from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 +from unity_sps_ogc_processes_api.models.execute200_response import ( # noqa: F401 + Execute200Response, +) +from unity_sps_ogc_processes_api.models.execute200_response1 import ( # noqa: F401 + Execute200Response1, +) +from unity_sps_ogc_processes_api.models.execute_workflows import ( # noqa: F401 + ExecuteWorkflows, +) +from unity_sps_ogc_processes_api.models.process import Process # noqa: F401 +from unity_sps_ogc_processes_api.models.process_list import ProcessList # noqa: F401 +from unity_sps_ogc_processes_api.models.processes_list import ( # noqa: F401 + ProcessesList, +) +from unity_sps_ogc_processes_api.models.status_info import StatusInfo # noqa: F401 + + +def test_execute(client: TestClient): + """Test case for execute + + execute a process. + """ + ExecuteWorkflows() + # uncomment below to make a request + # response = client.request( + # "POST", + # "/processes/{processId}/execution".format(processId=unity_sps_ogc_processes_api.ProcessesList()), + # headers=headers, + # json=execute_workflows, + # params=params, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 + + +def test_get_process_description(client: TestClient): + """Test case for get_process_description + + retrieve a process description + """ + + # uncomment below to make a request + # response = client.request( + # "GET", + # "/processes/{processId}".format(processId=unity_sps_ogc_processes_api.ProcessesList()), + # headers=headers, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 + + +def test_get_processes(client: TestClient): + """Test case for get_processes + + retrieve the list of available processes + """ + + # uncomment below to make a request + # response = client.request( + # "GET", + # "/processes", + # headers=headers, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 diff --git a/openapi-generator-config.yaml b/openapi-generator-config.yaml new file mode 100644 index 0000000..09aaccf --- /dev/null +++ b/openapi-generator-config.yaml @@ -0,0 +1,2 @@ +additionalProperties: + packageName: unity_sps_ogc_processes_api From 95de49c3c49ebc42ba4714e9c9cfc89a2b337351 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Wed, 14 Aug 2024 13:55:56 -0700 Subject: [PATCH 09/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/dru_api.py | 16 ++++++++++++++++ .../models/input_description.py | 4 +--- 2 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 app-cp/src/openapi_server/impl/dru_api.py diff --git a/app-cp/src/openapi_server/impl/dru_api.py b/app-cp/src/openapi_server/impl/dru_api.py new file mode 100644 index 0000000..3e21755 --- /dev/null +++ b/app-cp/src/openapi_server/impl/dru_api.py @@ -0,0 +1,16 @@ +from unity_sps_ogc_processes_api.apis.dru_api_base import BaseDRUApi +from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg + + +class DRUApiImpl(BaseDRUApi): + def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> None: + # Implement deploy logic here + pass + + def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: + # Implement replace logic here + pass + + def undeploy(self, processId: str) -> None: + # Implement undeploy logic here + pass diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_description.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_description.py index eb803ce..99534d7 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_description.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_description.py @@ -45,9 +45,7 @@ class InputDescription(BaseModel): metadata: Optional[List[Metadata]] = None schema: ModelSchema = Field(alias="schema") min_occurs: Optional[StrictInt] = Field(default=1, alias="minOccurs") - max_occurs: Optional[InputDescriptionAllOfMaxOccurs] = Field( - default=None, alias="maxOccurs" - ) + max_occurs: Optional[StrictInt] = Field(alias="maxOccurs") value_passing: Optional[List[StrictStr]] = Field(default=None, alias="valuePassing") __properties: ClassVar[List[str]] = [ "title", From dc4a99475bd37f97d99721483a3e248bd7f610df Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Wed, 14 Aug 2024 18:16:29 -0700 Subject: [PATCH 10/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/api_api.py | 7 + app-cp/src/openapi_server/database/crud.py | 61 +++++ app-cp/src/openapi_server/database/models.py | 52 +++++ .../openapi_server/impl/conformance_api.py | 7 + app-cp/src/openapi_server/impl/dru_api.py | 8 +- app-cp/src/openapi_server/impl/jobs_api.py | 15 ++ .../openapi_server/impl/landing_page_api.py | 7 + .../src/openapi_server/impl/processes_api.py | 11 + .../models/fields_modifiers_properties.py | 170 ++------------ .../models/format.py | 10 +- .../models/input.py | 178 +++------------ .../models/input_description.py | 18 +- .../models/metadata.py | 176 +++----------- .../models/model_schema.py | 163 +++---------- .../models/ogcapppkg_execution_unit.py | 215 ++++-------------- .../models/output_description.py | 10 +- .../models/qualified_input_value.py | 16 +- .../models/qualified_input_value1.py | 16 +- .../models/qualified_input_value_workflows.py | 14 +- app-cp/tests/test_dru_api.py | 206 ++++++++++++----- app/schemas/ogc_processes.py | 11 +- unity-test/conftest.py | 9 +- unity-test/test_main.py | 5 +- 23 files changed, 520 insertions(+), 865 deletions(-) create mode 100644 app-cp/src/openapi_server/api_api.py create mode 100644 app-cp/src/openapi_server/database/crud.py create mode 100644 app-cp/src/openapi_server/database/models.py create mode 100644 app-cp/src/openapi_server/impl/conformance_api.py create mode 100644 app-cp/src/openapi_server/impl/jobs_api.py create mode 100644 app-cp/src/openapi_server/impl/landing_page_api.py create mode 100644 app-cp/src/openapi_server/impl/processes_api.py diff --git a/app-cp/src/openapi_server/api_api.py b/app-cp/src/openapi_server/api_api.py new file mode 100644 index 0000000..7b47ce0 --- /dev/null +++ b/app-cp/src/openapi_server/api_api.py @@ -0,0 +1,7 @@ +from unity_sps_ogc_processes_api.apis.api_api_base import BaseAPIApi + + +class APIApiImpl(BaseAPIApi): + def get_api(self): + # Implement get_api logic here + pass diff --git a/app-cp/src/openapi_server/database/crud.py b/app-cp/src/openapi_server/database/crud.py new file mode 100644 index 0000000..f316c16 --- /dev/null +++ b/app-cp/src/openapi_server/database/crud.py @@ -0,0 +1,61 @@ +from sqlalchemy.orm import Session + +from unity_sps_ogc_processes_api.models import ogcapppkg + +from . import models + + +def create_process(db: Session, process: ogcapppkg.Ogcapppkg): + db_process = models.Process(**process.dict()) + db.add(db_process) + db.commit() + db.refresh(db_process) + return db_process + + +def get_processes(db: Session, skip: int = 0, limit: int = 100): + return db.query(models.Process).offset(skip).limit(limit).all() + + +def get_process(db: Session, process_id: str): + return db.query(models.Process).filter(models.Process.id == process_id).one() + + +def delete_process(db: Session, process: models.Process): + db.delete(process) + db.commit() + + +def create_job(db: Session, job_data: dict): + db_job = models.Job(**job_data) + db.add(db_job) + db.commit() + db.refresh(db_job) + return db_job + + +def update_job(db: Session, job_id: str, job_data: dict): + db_job = db.query(models.Job).filter(models.Job.jobID == job_id).one() + for key, value in job_data.items(): + if hasattr(db_job, key): + setattr(db_job, key, value) + db.commit() + db.refresh(db_job) + return db_job + + +def get_jobs(db: Session, skip: int = 0, limit: int = 100): + return db.query(models.Job).offset(skip).limit(limit).all() + + +def get_job(db: Session, job_id: str): + return db.query(models.Job).filter(models.Job.jobID == job_id).one() + + +def get_results(db: Session, job_id: str): + return db.query(models.Result).filter(models.Result.jobID == job_id).all() + + +def delete_job(db: Session, job: models.Job): + db.delete(job) + db.commit() diff --git a/app-cp/src/openapi_server/database/models.py b/app-cp/src/openapi_server/database/models.py new file mode 100644 index 0000000..a1a2308 --- /dev/null +++ b/app-cp/src/openapi_server/database/models.py @@ -0,0 +1,52 @@ +from sqlalchemy import JSON, Column, DateTime, ForeignKey, Integer, String +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import relationship +from sqlalchemy.sql import func + +Base = declarative_base() + + +class Process(Base): + __tablename__ = "processes" + _id = Column(Integer, primary_key=True) + id = Column(String, index=True, unique=True, nullable=False) + title = Column(String) + description = Column(String) + keywords = Column(JSON) + version = Column(String) + jobControlOptions = Column(JSON) + links = Column(JSON) + inputs = Column(JSON) + outputs = Column(JSON) + metadata = Column(JSON) + jobs = relationship("Job", back_populates="process") + + +class Job(Base): + __tablename__ = "jobs" + _id = Column(Integer, primary_key=True) + jobID = Column(String, index=True, unique=True, nullable=False) + processID = Column(String, ForeignKey("processes.id")) + process = relationship("Process", back_populates="jobs") + type = Column(String) + status = Column(String) + message = Column(String, nullable=True) + exception = Column(JSON, nullable=True) + created = Column(DateTime(timezone=True), default=func.now()) + started = Column(DateTime(timezone=True), nullable=True) + finished = Column(DateTime(timezone=True), nullable=True) + updated = Column(DateTime(timezone=True), nullable=True) + progress = Column(Integer) + links = Column(JSON, nullable=True) + inputs = Column(JSON) + outputs = Column(JSON) + subscriber = Column(JSON) + results = relationship("Result", back_populates="job") + + +class Result(Base): + __tablename__ = "results" + _id = Column(Integer, primary_key=True) + jobID = Column(String, ForeignKey("jobs.jobID")) + job = relationship("Job", back_populates="results") + root = Column(JSON) diff --git a/app-cp/src/openapi_server/impl/conformance_api.py b/app-cp/src/openapi_server/impl/conformance_api.py new file mode 100644 index 0000000..43a7d6d --- /dev/null +++ b/app-cp/src/openapi_server/impl/conformance_api.py @@ -0,0 +1,7 @@ +from unity_sps_ogc_processes_api.apis.conformance_api_base import BaseConformanceApi + + +class ConformanceApiImpl(BaseConformanceApi): + def get_conformance(self): + # Implement get_conformance logic here + pass diff --git a/app-cp/src/openapi_server/impl/dru_api.py b/app-cp/src/openapi_server/impl/dru_api.py index 3e21755..16d7ae5 100644 --- a/app-cp/src/openapi_server/impl/dru_api.py +++ b/app-cp/src/openapi_server/impl/dru_api.py @@ -1,11 +1,15 @@ +from fastapi import Response, status + from unity_sps_ogc_processes_api.apis.dru_api_base import BaseDRUApi from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg class DRUApiImpl(BaseDRUApi): - def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> None: + def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: # Implement deploy logic here - pass + + # Return a successful response with status code 201 + return Response(status_code=status.HTTP_201_CREATED) def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: # Implement replace logic here diff --git a/app-cp/src/openapi_server/impl/jobs_api.py b/app-cp/src/openapi_server/impl/jobs_api.py new file mode 100644 index 0000000..25fab14 --- /dev/null +++ b/app-cp/src/openapi_server/impl/jobs_api.py @@ -0,0 +1,15 @@ +from unity_sps_ogc_processes_api.apis.jobs_api_base import BaseJobsApi + + +class JobsApiImpl(BaseJobsApi): + def get_job(self, jobId): + # Implement get_job logic here + pass + + def get_jobs(self): + # Implement get_jobs logic here + pass + + def post_job(self, execute): + # Implement post_job logic here + pass diff --git a/app-cp/src/openapi_server/impl/landing_page_api.py b/app-cp/src/openapi_server/impl/landing_page_api.py new file mode 100644 index 0000000..d60c820 --- /dev/null +++ b/app-cp/src/openapi_server/impl/landing_page_api.py @@ -0,0 +1,7 @@ +from unity_sps_ogc_processes_api.apis.landing_page_api_base import BaseLandingPageApi + + +class LandingPageApiImpl(BaseLandingPageApi): + def get_landing_page(self): + # Implement get_landing_page logic here + pass diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py new file mode 100644 index 0000000..870ee03 --- /dev/null +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -0,0 +1,11 @@ +from unity_sps_ogc_processes_api.apis.processes_api_base import BaseProcessesApi + + +class ProcessesApiImpl(BaseProcessesApi): + def get_process(self, processId): + # Implement get_process logic here + pass + + def get_processes(self): + # Implement get_processes logic here + pass diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py b/app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py index e5c7919..1546bfe 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py @@ -1,167 +1,43 @@ -# coding: utf-8 - -""" - OGC API - Processes - - Example API Definition for OGC API - Processes - - The version of the OpenAPI document: 0.1 - Contact: info@ogc.org - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - from __future__ import annotations import json import pprint -import re # noqa: F401 -from typing import Dict, List, Optional, Union - -from pydantic import BaseModel, StrictStr, ValidationError, field_validator -from typing_extensions import Literal +from typing import Any, Dict, List, Union -try: - from typing import Self -except ImportError: - from typing_extensions import Self +from pydantic import RootModel, StrictStr, model_validator -FIELDSMODIFIERSPROPERTIES_ONE_OF_SCHEMAS = ["Dict[str, str]", "List[str]"] +class FieldsModifiersProperties(RootModel): + root: Union[Dict[str, StrictStr], List[StrictStr]] -class FieldsModifiersProperties(BaseModel): - """ - FieldsModifiersProperties - """ - - # data type: Dict[str, str] - oneof_schema_1_validator: Optional[Dict[str, StrictStr]] = None - # data type: List[str] - oneof_schema_2_validator: Optional[List[StrictStr]] = None - actual_instance: Optional[Union[Dict[str, str], List[str]]] = None - one_of_schemas: List[str] = Literal["Dict[str, str]", "List[str]"] - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) - if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator("actual_instance") - def actual_instance_must_validate_oneof(cls, v): - instance = FieldsModifiersProperties.model_construct() - error_messages = [] - match = 0 - # validate data type: Dict[str, str] - try: - instance.oneof_schema_1_validator = v - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # validate data type: List[str] - try: - instance.oneof_schema_2_validator = v - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when setting `actual_instance` in FieldsModifiersProperties with oneOf schemas: Dict[str, str], List[str]. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when setting `actual_instance` in FieldsModifiersProperties with oneOf schemas: Dict[str, str], List[str]. Details: " - + ", ".join(error_messages) - ) - else: - return v - + @model_validator(mode="before") @classmethod - def from_dict(cls, obj: dict) -> Self: - return cls.from_json(json.dumps(obj)) + def validate_type(cls, value): + if isinstance(value, dict): + return {k: StrictStr(v) for k, v in value.items()} + elif isinstance(value, list): + return [StrictStr(item) for item in value] + raise ValueError(f"Invalid type for FieldsModifiersProperties: {type(value)}") @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 + def from_dict(cls, obj: Dict[str, Any]) -> FieldsModifiersProperties: + return cls(root=cls.validate_type(obj)) - # deserialize data into Dict[str, str] - try: - # validation - instance.oneof_schema_1_validator = json.loads(json_str) - # assign value to actual_instance - instance.actual_instance = instance.oneof_schema_1_validator - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into List[str] - try: - # validation - instance.oneof_schema_2_validator = json.loads(json_str) - # assign value to actual_instance - instance.actual_instance = instance.oneof_schema_2_validator - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) + @classmethod + def from_json(cls, json_str: str) -> FieldsModifiersProperties: + return cls.from_dict(json.loads(json_str)) - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when deserializing the JSON string into FieldsModifiersProperties with oneOf schemas: Dict[str, str], List[str]. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when deserializing the JSON string into FieldsModifiersProperties with oneOf schemas: Dict[str, str], List[str]. Details: " - + ", ".join(error_messages) - ) - else: - return instance + def to_dict(self) -> Dict[str, Any]: + return self.root def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - to_json = getattr(self.actual_instance, "to_json", None) - if callable(to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) + return json.dumps(self.to_dict()) - def to_dict(self) -> Dict: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None + def __getattr__(self, name: str) -> Any: + return getattr(self.root, name) - to_dict = getattr(self.actual_instance, "to_dict", None) - if callable(to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance + def __repr__(self) -> str: + return f"FieldsModifiersProperties({self.root!r})" def to_str(self) -> str: - """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/format.py b/app-cp/src/unity_sps_ogc_processes_api/models/format.py index eea085c..c795faf 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/format.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/format.py @@ -37,7 +37,7 @@ class Format(BaseModel): media_type: Optional[StrictStr] = Field(default=None, alias="mediaType") encoding: Optional[StrictStr] = None - schema: Optional[FormatSchema] = Field(default=None, alias="schema") + schema_: Optional[FormatSchema] = Field(default=None, alias="schema") __properties: ClassVar[List[str]] = ["mediaType", "encoding", "schema"] model_config = { @@ -76,8 +76,8 @@ def to_dict(self) -> Dict[str, Any]: exclude_none=True, ) # override the default output from pydantic by calling `to_dict()` of schema - if self.schema: - _dict["schema"] = self.schema.to_dict() + if self.schema_: + _dict["schema"] = self.schema_.to_dict() return _dict @classmethod @@ -94,9 +94,7 @@ def from_dict(cls, obj: Dict) -> Self: "mediaType": obj.get("mediaType"), "encoding": obj.get("encoding"), "schema": ( - FormatSchema.from_dict(obj.get("schema")) - if obj.get("schema") is not None - else None + FormatSchema.from_dict(obj.get("schema")) if obj.get("schema") is not None else None ), } ) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input.py b/app-cp/src/unity_sps_ogc_processes_api/models/input.py index 7e695d8..428cdcb 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input.py @@ -1,167 +1,55 @@ -# coding: utf-8 - -""" - OGC API - Processes - - Example API Definition for OGC API - Processes - - The version of the OpenAPI document: 0.1 - Contact: info@ogc.org - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - from __future__ import annotations import json import pprint -import re # noqa: F401 -from typing import Dict, List, Optional, Union +from typing import Any, Dict, List, Union -from pydantic import BaseModel, ValidationError, field_validator -from typing_extensions import Literal +from pydantic import RootModel, ValidationError, model_validator from unity_sps_ogc_processes_api.models.inline_or_ref_data1 import InlineOrRefData1 -try: - from typing import Self -except ImportError: - from typing_extensions import Self - -INPUT_ONE_OF_SCHEMAS = ["InlineOrRefData1", "List[InlineOrRefData1]"] +class Input(RootModel): + root: Union[InlineOrRefData1, List[InlineOrRefData1]] -class Input(BaseModel): - """ - Input - """ - - # data type: InlineOrRefData1 - oneof_schema_1_validator: Optional[InlineOrRefData1] = None - # data type: List[InlineOrRefData1] - oneof_schema_2_validator: Optional[List[InlineOrRefData1]] = None - actual_instance: Optional[Union[InlineOrRefData1, List[InlineOrRefData1]]] = None - one_of_schemas: List[str] = Literal["InlineOrRefData1", "List[InlineOrRefData1]"] - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) - if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator("actual_instance") - def actual_instance_must_validate_oneof(cls, v): - instance = Input.model_construct() - error_messages = [] - match = 0 - # validate data type: InlineOrRefData1 - if not isinstance(v, InlineOrRefData1): - error_messages.append( - f"Error! Input type `{type(v)}` is not `InlineOrRefData1`" - ) - else: - match += 1 - # validate data type: List[InlineOrRefData1] - try: - instance.oneof_schema_2_validator = v - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when setting `actual_instance` in Input with oneOf schemas: InlineOrRefData1, List[InlineOrRefData1]. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when setting `actual_instance` in Input with oneOf schemas: InlineOrRefData1, List[InlineOrRefData1]. Details: " - + ", ".join(error_messages) - ) - else: - return v - + @model_validator(mode="before") @classmethod - def from_dict(cls, obj: dict) -> Self: - return cls.from_json(json.dumps(obj)) + def validate_type(cls, value): + if isinstance(value, dict): + try: + return InlineOrRefData1(**value) + except ValidationError: + pass + elif isinstance(value, list): + try: + return [InlineOrRefData1(**item) for item in value] + except ValidationError: + pass + elif isinstance(value, InlineOrRefData1): + return value + raise ValueError(f"Invalid type for Input: {type(value)}") @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 + def from_dict(cls, obj: Dict[str, Any]) -> Input: + return cls(root=cls.validate_type(obj)) - # deserialize data into InlineOrRefData1 - try: - instance.actual_instance = InlineOrRefData1.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into List[InlineOrRefData1] - try: - # validation - instance.oneof_schema_2_validator = json.loads(json_str) - # assign value to actual_instance - instance.actual_instance = instance.oneof_schema_2_validator - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) + @classmethod + def from_json(cls, json_str: str) -> Input: + return cls.from_dict(json.loads(json_str)) - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when deserializing the JSON string into Input with oneOf schemas: InlineOrRefData1, List[InlineOrRefData1]. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when deserializing the JSON string into Input with oneOf schemas: InlineOrRefData1, List[InlineOrRefData1]. Details: " - + ", ".join(error_messages) - ) - else: - return instance + def to_dict(self) -> Dict[str, Any]: + if isinstance(self.root, list): + return [item.model_dump() for item in self.root] + return self.root.model_dump() def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - to_json = getattr(self.actual_instance, "to_json", None) - if callable(to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) + return json.dumps(self.to_dict()) - def to_dict(self) -> Dict: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None + def __getattr__(self, name: str) -> Any: + return getattr(self.root, name) - to_dict = getattr(self.actual_instance, "to_dict", None) - if callable(to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance + def __repr__(self) -> str: + return f"Input({self.root!r})" def to_str(self) -> str: - """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_description.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_description.py index 99534d7..69e9b1b 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_description.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_description.py @@ -43,7 +43,7 @@ class InputDescription(BaseModel): description: Optional[StrictStr] = None keywords: Optional[List[StrictStr]] = None metadata: Optional[List[Metadata]] = None - schema: ModelSchema = Field(alias="schema") + schema_: ModelSchema = Field(alias="schema") min_occurs: Optional[StrictInt] = Field(default=1, alias="minOccurs") max_occurs: Optional[StrictInt] = Field(alias="maxOccurs") value_passing: Optional[List[StrictStr]] = Field(default=None, alias="valuePassing") @@ -66,9 +66,7 @@ def value_passing_validate_enum(cls, value): for i in value: if i not in ("byValue", "byReference"): - raise ValueError( - "each list item must be one of ('byValue', 'byReference')" - ) + raise ValueError("each list item must be one of ('byValue', 'byReference')") return value model_config = { @@ -114,8 +112,8 @@ def to_dict(self) -> Dict[str, Any]: _items.append(_item.to_dict()) _dict["metadata"] = _items # override the default output from pydantic by calling `to_dict()` of schema - if self.schema: - _dict["schema"] = self.schema.to_dict() + if self.schema_: + _dict["schema"] = self.schema_.to_dict() # override the default output from pydantic by calling `to_dict()` of max_occurs if self.max_occurs: _dict["maxOccurs"] = self.max_occurs.to_dict() @@ -141,13 +139,9 @@ def from_dict(cls, obj: Dict) -> Self: else None ), "schema": ( - ModelSchema.from_dict(obj.get("schema")) - if obj.get("schema") is not None - else None - ), - "minOccurs": ( - obj.get("minOccurs") if obj.get("minOccurs") is not None else 1 + ModelSchema.from_dict(obj.get("schema")) if obj.get("schema") is not None else None ), + "minOccurs": (obj.get("minOccurs") if obj.get("minOccurs") is not None else 1), "maxOccurs": ( InputDescriptionAllOfMaxOccurs.from_dict(obj.get("maxOccurs")) if obj.get("maxOccurs") is not None diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/metadata.py b/app-cp/src/unity_sps_ogc_processes_api/models/metadata.py index a50719d..963f702 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/metadata.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/metadata.py @@ -1,166 +1,48 @@ -# coding: utf-8 - -""" - OGC API - Processes - - Example API Definition for OGC API - Processes - - The version of the OpenAPI document: 0.1 - Contact: info@ogc.org - Generated by OpenAPI Generator (https://openapi-generator.tech) - - Do not edit the class manually. -""" # noqa: E501 - - from __future__ import annotations import json -import pprint -import re # noqa: F401 -from typing import Dict, List, Optional, Union +from typing import Any, Dict, Union -from pydantic import BaseModel, ValidationError, field_validator -from typing_extensions import Literal +from pydantic import RootModel, ValidationError, model_validator from unity_sps_ogc_processes_api.models.metadata_one_of import MetadataOneOf from unity_sps_ogc_processes_api.models.metadata_one_of1 import MetadataOneOf1 -try: - from typing import Self -except ImportError: - from typing_extensions import Self - -METADATA_ONE_OF_SCHEMAS = ["MetadataOneOf", "MetadataOneOf1"] - -class Metadata(BaseModel): - """ - Metadata - """ - - # data type: MetadataOneOf - oneof_schema_1_validator: Optional[MetadataOneOf] = None - # data type: MetadataOneOf1 - oneof_schema_2_validator: Optional[MetadataOneOf1] = None - actual_instance: Optional[Union[MetadataOneOf, MetadataOneOf1]] = None - one_of_schemas: List[str] = Literal["MetadataOneOf", "MetadataOneOf1"] - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) - if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator("actual_instance") - def actual_instance_must_validate_oneof(cls, v): - Metadata.model_construct() - error_messages = [] - match = 0 - # validate data type: MetadataOneOf - if not isinstance(v, MetadataOneOf): - error_messages.append( - f"Error! Input type `{type(v)}` is not `MetadataOneOf`" - ) - else: - match += 1 - # validate data type: MetadataOneOf1 - if not isinstance(v, MetadataOneOf1): - error_messages.append( - f"Error! Input type `{type(v)}` is not `MetadataOneOf1`" - ) - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when setting `actual_instance` in Metadata with oneOf schemas: MetadataOneOf, MetadataOneOf1. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when setting `actual_instance` in Metadata with oneOf schemas: MetadataOneOf, MetadataOneOf1. Details: " - + ", ".join(error_messages) - ) - else: - return v +class Metadata(RootModel): + root: Union[MetadataOneOf, MetadataOneOf1] + @model_validator(mode="before") @classmethod - def from_dict(cls, obj: dict) -> Self: - return cls.from_json(json.dumps(obj)) + def validate_type(cls, value): + if isinstance(value, dict): + try: + return MetadataOneOf(**value) + except ValidationError: + try: + return MetadataOneOf1(**value) + except ValidationError: + pass + elif isinstance(value, (MetadataOneOf, MetadataOneOf1)): + return value + raise ValueError(f"Invalid type for Metadata: {type(value)}") @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 + def from_dict(cls, obj: Dict[str, Any]) -> Metadata: + return cls(root=cls.validate_type(obj)) - # deserialize data into MetadataOneOf - try: - instance.actual_instance = MetadataOneOf.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into MetadataOneOf1 - try: - instance.actual_instance = MetadataOneOf1.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) + @classmethod + def from_json(cls, json_str: str) -> Metadata: + return cls.from_dict(json.loads(json_str)) - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when deserializing the JSON string into Metadata with oneOf schemas: MetadataOneOf, MetadataOneOf1. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when deserializing the JSON string into Metadata with oneOf schemas: MetadataOneOf, MetadataOneOf1. Details: " - + ", ".join(error_messages) - ) - else: - return instance + def to_dict(self) -> Dict[str, Any]: + return self.root.model_dump() def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - to_json = getattr(self.actual_instance, "to_json", None) - if callable(to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Dict: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None + return json.dumps(self.to_dict()) - to_dict = getattr(self.actual_instance, "to_dict", None) - if callable(to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance + def __getattr__(self, name: str) -> Any: + return getattr(self.root, name) - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.model_dump()) + def __repr__(self) -> str: + return f"Metadata({self.root!r})" diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/model_schema.py b/app-cp/src/unity_sps_ogc_processes_api/models/model_schema.py index 0efcc3d..a9e7f42 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/model_schema.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/model_schema.py @@ -17,146 +17,51 @@ import json import pprint -import re # noqa: F401 -from typing import Dict, List, Optional, Union +from typing import Any, Dict, Union -from pydantic import BaseModel, ValidationError, field_validator -from typing_extensions import Literal +from pydantic import RootModel, ValidationError, model_validator from unity_sps_ogc_processes_api.models.reference import Reference from unity_sps_ogc_processes_api.models.schema_one_of import SchemaOneOf -try: - from typing import Self -except ImportError: - from typing_extensions import Self - -MODELSCHEMA_ONE_OF_SCHEMAS = ["Reference", "SchemaOneOf"] - - -class ModelSchema(BaseModel): - """ - ModelSchema - """ - - # data type: Reference - oneof_schema_1_validator: Optional[Reference] = None - # data type: SchemaOneOf - oneof_schema_2_validator: Optional[SchemaOneOf] = None - actual_instance: Optional[Union[Reference, SchemaOneOf]] = None - one_of_schemas: List[str] = Literal["Reference", "SchemaOneOf"] - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) - if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator("actual_instance") - def actual_instance_must_validate_oneof(cls, v): - ModelSchema.model_construct() - error_messages = [] - match = 0 - # validate data type: Reference - if not isinstance(v, Reference): - error_messages.append(f"Error! Input type `{type(v)}` is not `Reference`") - else: - match += 1 - # validate data type: SchemaOneOf - if not isinstance(v, SchemaOneOf): - error_messages.append(f"Error! Input type `{type(v)}` is not `SchemaOneOf`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when setting `actual_instance` in ModelSchema with oneOf schemas: Reference, SchemaOneOf. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when setting `actual_instance` in ModelSchema with oneOf schemas: Reference, SchemaOneOf. Details: " - + ", ".join(error_messages) - ) - else: - return v + +class ModelSchema(RootModel): + root: Union[Reference, SchemaOneOf] + + @model_validator(mode="before") + @classmethod + def validate_type(cls, value): + if isinstance(value, dict): + try: + return Reference(**value) + except ValidationError: + try: + return SchemaOneOf(**value) + except ValidationError: + pass + elif isinstance(value, (Reference, SchemaOneOf)): + return value + raise ValueError(f"Invalid type for ModelSchema: {type(value)}") @classmethod - def from_dict(cls, obj: dict) -> Self: - return cls.from_json(json.dumps(obj)) + def from_dict(cls, obj: Dict[str, Any]) -> ModelSchema: + return cls(root=cls.validate_type(obj)) @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into Reference - try: - instance.actual_instance = Reference.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into SchemaOneOf - try: - instance.actual_instance = SchemaOneOf.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when deserializing the JSON string into ModelSchema with oneOf schemas: Reference, SchemaOneOf. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when deserializing the JSON string into ModelSchema with oneOf schemas: Reference, SchemaOneOf. Details: " - + ", ".join(error_messages) - ) - else: - return instance + def from_json(cls, json_str: str) -> ModelSchema: + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + return self.root.model_dump() def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - to_json = getattr(self.actual_instance, "to_json", None) - if callable(to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Dict: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - to_dict = getattr(self.actual_instance, "to_dict", None) - if callable(to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance + return json.dumps(self.to_dict()) + + def __getattr__(self, name: str) -> Any: + return getattr(self.root, name) + + def __repr__(self) -> str: + return f"ModelSchema({self.root!r})" def to_str(self) -> str: - """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py b/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py index 2aca797..fa239fe 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py @@ -17,191 +17,62 @@ import json import pprint -import re # noqa: F401 -from typing import Dict, List, Optional, Union +from typing import Any, Dict, List, Union -from pydantic import BaseModel, ValidationError, field_validator -from typing_extensions import Literal +from pydantic import RootModel, ValidationError, model_validator from unity_sps_ogc_processes_api.models.execution_unit import ExecutionUnit from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.ogcapppkg_array_inner import OgcapppkgArrayInner from unity_sps_ogc_processes_api.models.qualified_input_value import QualifiedInputValue -try: - from typing import Self -except ImportError: - from typing_extensions import Self - -OGCAPPPKGEXECUTIONUNIT_ONE_OF_SCHEMAS = [ - "ExecutionUnit", - "Link", - "List[OgcapppkgArrayInner]", - "QualifiedInputValue", -] - - -class OgcapppkgExecutionUnit(BaseModel): - """ - OgcapppkgExecutionUnit - """ - - # data type: ExecutionUnit - oneof_schema_1_validator: Optional[ExecutionUnit] = None - # data type: Link - oneof_schema_2_validator: Optional[Link] = None - # data type: QualifiedInputValue - oneof_schema_3_validator: Optional[QualifiedInputValue] = None - # data type: List[OgcapppkgArrayInner] - oneof_schema_4_validator: Optional[List[OgcapppkgArrayInner]] = None - actual_instance: Optional[ - Union[ExecutionUnit, Link, List[OgcapppkgArrayInner], QualifiedInputValue] - ] = None - one_of_schemas: List[str] = Literal[ - "ExecutionUnit", "Link", "List[OgcapppkgArrayInner]", "QualifiedInputValue" - ] - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) - if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator("actual_instance") - def actual_instance_must_validate_oneof(cls, v): - instance = OgcapppkgExecutionUnit.model_construct() - error_messages = [] - match = 0 - # validate data type: ExecutionUnit - if not isinstance(v, ExecutionUnit): - error_messages.append( - f"Error! Input type `{type(v)}` is not `ExecutionUnit`" - ) - else: - match += 1 - # validate data type: Link - if not isinstance(v, Link): - error_messages.append(f"Error! Input type `{type(v)}` is not `Link`") - else: - match += 1 - # validate data type: QualifiedInputValue - if not isinstance(v, QualifiedInputValue): - error_messages.append( - f"Error! Input type `{type(v)}` is not `QualifiedInputValue`" - ) - else: - match += 1 - # validate data type: List[OgcapppkgArrayInner] - try: - instance.oneof_schema_4_validator = v - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when setting `actual_instance` in OgcapppkgExecutionUnit with oneOf schemas: ExecutionUnit, Link, List[OgcapppkgArrayInner], QualifiedInputValue. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when setting `actual_instance` in OgcapppkgExecutionUnit with oneOf schemas: ExecutionUnit, Link, List[OgcapppkgArrayInner], QualifiedInputValue. Details: " - + ", ".join(error_messages) - ) - else: - return v + +class OgcapppkgExecutionUnit(RootModel): + root: Union[ExecutionUnit, Link, List[OgcapppkgArrayInner], QualifiedInputValue] + + @model_validator(mode="before") + @classmethod + def validate_type(cls, value): + if isinstance(value, dict): + for schema in [ExecutionUnit, Link, QualifiedInputValue]: + try: + return schema(**value) + except ValidationError: + pass + try: + return [OgcapppkgArrayInner(**item) for item in value] + except ValidationError: + pass + elif isinstance(value, (ExecutionUnit, Link, QualifiedInputValue)): + return value + elif isinstance(value, list): + try: + return [OgcapppkgArrayInner(**item) for item in value] + except ValidationError: + pass + raise ValueError(f"Invalid type for OgcapppkgExecutionUnit: {type(value)}") @classmethod - def from_dict(cls, obj: dict) -> Self: - return cls.from_json(json.dumps(obj)) + def from_dict(cls, obj: Dict[str, Any]) -> OgcapppkgExecutionUnit: + return cls(root=cls.validate_type(obj)) @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into ExecutionUnit - try: - instance.actual_instance = ExecutionUnit.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into Link - try: - instance.actual_instance = Link.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into QualifiedInputValue - try: - instance.actual_instance = QualifiedInputValue.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into List[OgcapppkgArrayInner] - try: - # validation - instance.oneof_schema_4_validator = json.loads(json_str) - # assign value to actual_instance - instance.actual_instance = instance.oneof_schema_4_validator - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when deserializing the JSON string into OgcapppkgExecutionUnit with oneOf schemas: ExecutionUnit, Link, List[OgcapppkgArrayInner], QualifiedInputValue. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when deserializing the JSON string into OgcapppkgExecutionUnit with oneOf schemas: ExecutionUnit, Link, List[OgcapppkgArrayInner], QualifiedInputValue. Details: " - + ", ".join(error_messages) - ) - else: - return instance + def from_json(cls, json_str: str) -> OgcapppkgExecutionUnit: + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + if isinstance(self.root, list): + return [item.model_dump() for item in self.root] + return self.root.model_dump() def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - to_json = getattr(self.actual_instance, "to_json", None) - if callable(to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Dict: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - to_dict = getattr(self.actual_instance, "to_dict", None) - if callable(to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance + return json.dumps(self.to_dict()) + + def __getattr__(self, name: str) -> Any: + return getattr(self.root, name) + + def __repr__(self) -> str: + return f"OgcapppkgExecutionUnit({self.root!r})" def to_str(self) -> str: - """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/output_description.py b/app-cp/src/unity_sps_ogc_processes_api/models/output_description.py index 7ea41d5..4b81393 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/output_description.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/output_description.py @@ -40,7 +40,7 @@ class OutputDescription(BaseModel): description: Optional[StrictStr] = None keywords: Optional[List[StrictStr]] = None metadata: Optional[List[Metadata]] = None - schema: ModelSchema = Field(alias="schema") + schema_: ModelSchema = Field(alias="schema") __properties: ClassVar[List[str]] = [ "title", "description", @@ -92,8 +92,8 @@ def to_dict(self) -> Dict[str, Any]: _items.append(_item.to_dict()) _dict["metadata"] = _items # override the default output from pydantic by calling `to_dict()` of schema - if self.schema: - _dict["schema"] = self.schema.to_dict() + if self.schema_: + _dict["schema"] = self.schema_.to_dict() return _dict @classmethod @@ -116,9 +116,7 @@ def from_dict(cls, obj: Dict) -> Self: else None ), "schema": ( - ModelSchema.from_dict(obj.get("schema")) - if obj.get("schema") is not None - else None + ModelSchema.from_dict(obj.get("schema")) if obj.get("schema") is not None else None ), } ) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value.py b/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value.py index 83abf4b..d25f160 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value.py @@ -38,7 +38,7 @@ class QualifiedInputValue(BaseModel): media_type: Optional[StrictStr] = Field(default=None, alias="mediaType") encoding: Optional[StrictStr] = None - schema: Optional[FormatSchema] = Field(default=None, alias="schema") + schema_: Optional[FormatSchema] = Field(default=None, alias="schema") value: InputValue __properties: ClassVar[List[str]] = ["mediaType", "encoding", "schema", "value"] @@ -78,8 +78,8 @@ def to_dict(self) -> Dict[str, Any]: exclude_none=True, ) # override the default output from pydantic by calling `to_dict()` of schema - if self.schema: - _dict["schema"] = self.schema.to_dict() + if self.schema_: + _dict["schema"] = self.schema_.to_dict() # override the default output from pydantic by calling `to_dict()` of value if self.value: _dict["value"] = self.value.to_dict() @@ -99,15 +99,9 @@ def from_dict(cls, obj: Dict) -> Self: "mediaType": obj.get("mediaType"), "encoding": obj.get("encoding"), "schema": ( - FormatSchema.from_dict(obj.get("schema")) - if obj.get("schema") is not None - else None - ), - "value": ( - InputValue.from_dict(obj.get("value")) - if obj.get("value") is not None - else None + FormatSchema.from_dict(obj.get("schema")) if obj.get("schema") is not None else None ), + "value": (InputValue.from_dict(obj.get("value")) if obj.get("value") is not None else None), } ) return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py b/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py index a7544fc..d82bd76 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py @@ -38,7 +38,7 @@ class QualifiedInputValue1(BaseModel): media_type: Optional[StrictStr] = Field(default=None, alias="mediaType") encoding: Optional[StrictStr] = None - schema: Optional[FormatSchema] = Field(default=None, alias="schema") + schema_: Optional[FormatSchema] = Field(default=None, alias="schema") value: InputValue1 __properties: ClassVar[List[str]] = ["mediaType", "encoding", "schema", "value"] @@ -78,8 +78,8 @@ def to_dict(self) -> Dict[str, Any]: exclude_none=True, ) # override the default output from pydantic by calling `to_dict()` of schema - if self.schema: - _dict["schema"] = self.schema.to_dict() + if self.schema_: + _dict["schema"] = self.schema_.to_dict() # override the default output from pydantic by calling `to_dict()` of value if self.value: _dict["value"] = self.value.to_dict() @@ -99,15 +99,9 @@ def from_dict(cls, obj: Dict) -> Self: "mediaType": obj.get("mediaType"), "encoding": obj.get("encoding"), "schema": ( - FormatSchema.from_dict(obj.get("schema")) - if obj.get("schema") is not None - else None - ), - "value": ( - InputValue1.from_dict(obj.get("value")) - if obj.get("value") is not None - else None + FormatSchema.from_dict(obj.get("schema")) if obj.get("schema") is not None else None ), + "value": (InputValue1.from_dict(obj.get("value")) if obj.get("value") is not None else None), } ) return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py b/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py index 0336727..07feebd 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py @@ -40,7 +40,7 @@ class QualifiedInputValueWorkflows(BaseModel): media_type: Optional[StrictStr] = Field(default=None, alias="mediaType") encoding: Optional[StrictStr] = None - schema: Optional[FormatSchema] = Field(default=None, alias="schema") + schema_: Optional[FormatSchema] = Field(default=None, alias="schema") filter: Optional[StrictStr] = None properties: Optional[FieldsModifiersProperties] = None sort_by: Optional[List[StrictStr]] = Field(default=None, alias="sortBy") @@ -91,8 +91,8 @@ def to_dict(self) -> Dict[str, Any]: exclude_none=True, ) # override the default output from pydantic by calling `to_dict()` of schema - if self.schema: - _dict["schema"] = self.schema.to_dict() + if self.schema_: + _dict["schema"] = self.schema_.to_dict() # override the default output from pydantic by calling `to_dict()` of properties if self.properties: _dict["properties"] = self.properties.to_dict() @@ -115,9 +115,7 @@ def from_dict(cls, obj: Dict) -> Self: "mediaType": obj.get("mediaType"), "encoding": obj.get("encoding"), "schema": ( - FormatSchema.from_dict(obj.get("schema")) - if obj.get("schema") is not None - else None + FormatSchema.from_dict(obj.get("schema")) if obj.get("schema") is not None else None ), "filter": obj.get("filter"), "properties": ( @@ -127,9 +125,7 @@ def from_dict(cls, obj: Dict) -> Self: ), "sortBy": obj.get("sortBy"), "value": ( - InputValueWorkflows.from_dict(obj.get("value")) - if obj.get("value") is not None - else None + InputValueWorkflows.from_dict(obj.get("value")) if obj.get("value") is not None else None ), } ) diff --git a/app-cp/tests/test_dru_api.py b/app-cp/tests/test_dru_api.py index a4cdf42..0a30934 100644 --- a/app-cp/tests/test_dru_api.py +++ b/app-cp/tests/test_dru_api.py @@ -1,64 +1,152 @@ # coding: utf-8 +import pytest from fastapi.testclient import TestClient -from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 -from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg # noqa: F401 -from unity_sps_ogc_processes_api.models.processes_list import ( # noqa: F401 - ProcessesList, +from unity_sps_ogc_processes_api.models.execution_unit import ExecutionUnit +from unity_sps_ogc_processes_api.models.input_description import InputDescription +from unity_sps_ogc_processes_api.models.job_control_options import JobControlOptions +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.metadata import Metadata +from unity_sps_ogc_processes_api.models.metadata_one_of import MetadataOneOf +from unity_sps_ogc_processes_api.models.model_schema import ModelSchema +from unity_sps_ogc_processes_api.models.ogcapppkg import ( + Ogcapppkg, + OgcapppkgExecutionUnit, ) - - -def test_deploy(client: TestClient): - """Test case for deploy - - deploy a process. - """ - Ogcapppkg() - # uncomment below to make a request - # response = client.request( - # "POST", - # "/processes", - # headers=headers, - # json=ogcapppkg, - # params=params, - # ) - - # uncomment below to assert the status code of the HTTP response - # assert response.status_code == 200 - - -def test_replace(client: TestClient): - """Test case for replace - - replace a process. - """ - Ogcapppkg() - - # uncomment below to make a request - # response = client.request( - # "PUT", - # "/processes/{processId}".format(processId=unity_sps_ogc_processes_api.ProcessesList()), - # headers=headers, - # json=ogcapppkg, - # ) - - # uncomment below to assert the status code of the HTTP response - # assert response.status_code == 200 - - -def test_undeploy(client: TestClient): - """Test case for undeploy - - undeploy a process. - """ - - # uncomment below to make a request - # response = client.request( - # "DELETE", - # "/processes/{processId}".format(processId=unity_sps_ogc_processes_api.ProcessesList()), - # headers=headers, - # ) - - # uncomment below to assert the status code of the HTTP response - # assert response.status_code == 200 +from unity_sps_ogc_processes_api.models.output_description import OutputDescription +from unity_sps_ogc_processes_api.models.process import Process +from unity_sps_ogc_processes_api.models.reference import Reference + + +@pytest.fixture +def sample_ogcapppkg(): + return Ogcapppkg( + process_description=Process( + id="example-process-id", + version="1.0.0", + title="Example Process Title", + description="This process performs an example task.", + keywords=["example", "process", "OGC"], + metadata=[ + Metadata( + MetadataOneOf( + href="http://example.com/metadata", + rel="related", + type="application/json", + hreflang="en", + title="Example Metadata", + ) + ) + ], + job_control_options=[ + JobControlOptions.SYNC_MINUS_EXECUTE, + JobControlOptions.ASYNC_MINUS_EXECUTE, + ], + links=[ + Link( + href="http://example.com/process", + rel="self", + type="application/json", + hreflang="en", + title="Process Description", + ) + ], + inputs={ + "input1": InputDescription( + title="Input 1 Title", + description="Description of Input 1", + min_occurs=1, + max_occurs=1, + schema=ModelSchema(Reference(ref="#/components/schemas/ExampleSchema")), + formats=[ + { + "mediaType": "application/json", + "encoding": "UTF-8", + "schema": "http://json-schema.org/draft-07/schema#", + } + ], + ) + }, + outputs={ + "output1": OutputDescription( + title="Output 1 Title", + description="Description of Output 1", + schema=ModelSchema(actual_instance=Reference(ref="#/components/schemas/ExampleSchema")), + formats=[ + { + "mediaType": "application/json", + "encoding": "UTF-8", + "schema": "http://json-schema.org/draft-07/schema#", + } + ], + ) + }, + ), + execution_unit=OgcapppkgExecutionUnit( + ExecutionUnit( + type="docker", + image="example/image:latest", + deployment="cloud", + config={"cpu": "2", "memory": "4GiB", "env": {"EXAMPLE_ENV_VAR": "value"}}, + ) + ), + ) + + +def test_deploy(client: TestClient, sample_ogcapppkg): + """Test case for deploy""" + import pprint + + pprint.pprint(Ogcapppkg.model_validate(sample_ogcapppkg).model_dump(exclude_none=True, by_alias=True)) + + response = client.post( + "/processes", + json=sample_ogcapppkg.model_dump(exclude_none=True, by_alias=True), + ) + + assert response.status_code == 201 + print(response) + # pprint.pprint(response.json()) + # assert response.json()["id"] == "example-process-id" + # assert response.json()["title"] == "Example Process Title" + + +# def test_replace(client: TestClient, sample_ogcapppkg): +# """Test case for replace""" +# process_id = "test_process" +# response = client.put( +# f"/processes/{process_id}", +# json=sample_ogcapppkg.dict(), +# ) + +# assert response.status_code == 204 + + +# def test_undeploy(client: TestClient): +# """Test case for undeploy""" +# process_id = "test_process" +# response = client.delete(f"/processes/{process_id}") + +# assert response.status_code == 204 + + +# def test_deploy_conflict(client: TestClient, sample_ogcapppkg): +# """Test case for deploy when process already exists""" +# # First, deploy the process +# client.post("/processes", json=sample_ogcapppkg.dict()) + +# # Try to deploy the same process again +# response = client.post("/processes", json=sample_ogcapppkg.dict()) + +# assert response.status_code == 409 +# assert "already exists" in response.json()["detail"] + + +# def test_undeploy_not_found(client: TestClient): +# """Test case for undeploy when process doesn't exist""" +# process_id = "non_existent_process" +# response = client.delete(f"/processes/{process_id}") + +# assert response.status_code == 404 +# assert "not found" in response.json()["detail"] diff --git a/app/schemas/ogc_processes.py b/app/schemas/ogc_processes.py index 99b104c..169ee73 100644 --- a/app/schemas/ogc_processes.py +++ b/app/schemas/ogc_processes.py @@ -8,7 +8,16 @@ from enum import Enum from typing import Any, Dict, List, Optional, Set, Union -from pydantic import AnyUrl, BaseModel, ConfigDict, Field, PositiveFloat, RootModel, confloat, conint +from pydantic import ( + AnyUrl, + BaseModel, + ConfigDict, + Field, + PositiveFloat, + RootModel, + confloat, + conint, +) class BaseSchema(BaseModel): diff --git a/unity-test/conftest.py b/unity-test/conftest.py index 65e8776..08a260c 100644 --- a/unity-test/conftest.py +++ b/unity-test/conftest.py @@ -4,6 +4,10 @@ import fakeredis import pytest +from app.database import Base +from app.main import app, get_db, get_redis_locking_client, get_settings +from app.redis import RedisLock +from app.schemas.ogc_processes import Execute, Process, StatusCode, StatusInfo from fastapi import status from fastapi.encoders import jsonable_encoder from fastapi.testclient import TestClient @@ -11,11 +15,6 @@ from sqlalchemy.orm import sessionmaker from sqlalchemy.pool import StaticPool -from app.database import Base -from app.main import app, get_db, get_redis_locking_client, get_settings -from app.redis import RedisLock -from app.schemas.ogc_processes import Execute, Process, StatusCode, StatusInfo - settings = get_settings() SQLALCHEMY_DATABASE_URL = settings.DB_URL engine = create_engine( diff --git a/unity-test/test_main.py b/unity-test/test_main.py index 4c5c8d7..94fb1de 100644 --- a/unity-test/test_main.py +++ b/unity-test/test_main.py @@ -2,9 +2,6 @@ import os import pytest -from fastapi import status -from fastapi.encoders import jsonable_encoder - from app.schemas.ogc_processes import ( ConfClasses, Execute, @@ -17,6 +14,8 @@ StatusInfo, ) from app.schemas.unity_sps import HealthCheck +from fastapi import status +from fastapi.encoders import jsonable_encoder def test_get_landing_page(client): From 7abe7a840e79308605efd5cca1361c136fe35b4d Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Wed, 14 Aug 2024 18:19:12 -0700 Subject: [PATCH 11/60] feat: Initial autogenerated FastAPI implementation --- app-cp/tests/test_dru_api.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/app-cp/tests/test_dru_api.py b/app-cp/tests/test_dru_api.py index 0a30934..7e53f9b 100644 --- a/app-cp/tests/test_dru_api.py +++ b/app-cp/tests/test_dru_api.py @@ -58,7 +58,9 @@ def sample_ogcapppkg(): description="Description of Input 1", min_occurs=1, max_occurs=1, - schema=ModelSchema(Reference(ref="#/components/schemas/ExampleSchema")), + schema=ModelSchema( + Reference(ref="#/components/schemas/ExampleSchema") + ), formats=[ { "mediaType": "application/json", @@ -72,7 +74,11 @@ def sample_ogcapppkg(): "output1": OutputDescription( title="Output 1 Title", description="Description of Output 1", - schema=ModelSchema(actual_instance=Reference(ref="#/components/schemas/ExampleSchema")), + schema=ModelSchema( + actual_instance=Reference( + ref="#/components/schemas/ExampleSchema" + ) + ), formats=[ { "mediaType": "application/json", @@ -88,7 +94,11 @@ def sample_ogcapppkg(): type="docker", image="example/image:latest", deployment="cloud", - config={"cpu": "2", "memory": "4GiB", "env": {"EXAMPLE_ENV_VAR": "value"}}, + config={ + "cpu": "2", + "memory": "4GiB", + "env": {"EXAMPLE_ENV_VAR": "value"}, + }, ) ), ) @@ -96,20 +106,11 @@ def sample_ogcapppkg(): def test_deploy(client: TestClient, sample_ogcapppkg): """Test case for deploy""" - import pprint - - pprint.pprint(Ogcapppkg.model_validate(sample_ogcapppkg).model_dump(exclude_none=True, by_alias=True)) - response = client.post( "/processes", json=sample_ogcapppkg.model_dump(exclude_none=True, by_alias=True), ) - assert response.status_code == 201 - print(response) - # pprint.pprint(response.json()) - # assert response.json()["id"] == "example-process-id" - # assert response.json()["title"] == "Example Process Title" # def test_replace(client: TestClient, sample_ogcapppkg): From bb098f33cbc384d558980a07efce04bd73c25958 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 15 Aug 2024 09:13:23 -0700 Subject: [PATCH 12/60] feat: Initial autogenerated FastAPI implementation --- .../models/input.py | 43 +++- .../models/input_workflows.py | 238 +++++++----------- .../models/ogcapppkg_execution_unit.py | 19 +- 3 files changed, 135 insertions(+), 165 deletions(-) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input.py b/app-cp/src/unity_sps_ogc_processes_api/models/input.py index 428cdcb..f0c3271 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input.py @@ -4,28 +4,47 @@ import pprint from typing import Any, Dict, List, Union -from pydantic import RootModel, ValidationError, model_validator +from pydantic import RootModel, model_validator -from unity_sps_ogc_processes_api.models.inline_or_ref_data1 import InlineOrRefData1 +from unity_sps_ogc_processes_api.models.bbox1 import Bbox1 +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.qualified_input_value1 import ( + QualifiedInputValue1, +) class Input(RootModel): - root: Union[InlineOrRefData1, List[InlineOrRefData1]] + root: Union[ + Bbox1, + List[Any], + bool, + float, + int, + str, + Link, + QualifiedInputValue1, + List[ + Union[Bbox1, List[Any], bool, float, int, str, Link, QualifiedInputValue1] + ], + ] @model_validator(mode="before") @classmethod def validate_type(cls, value): if isinstance(value, dict): - try: - return InlineOrRefData1(**value) - except ValidationError: - pass + if all(k in value for k in ["mediaType", "encoding", "schema", "value"]): + return QualifiedInputValue1(**value) + if "bbox" in value: + return Bbox1(**value) + if "href" in value: + return Link(**value) elif isinstance(value, list): - try: - return [InlineOrRefData1(**item) for item in value] - except ValidationError: - pass - elif isinstance(value, InlineOrRefData1): + return [cls.validate_type(item) for item in value] + elif isinstance( + value, (bool, int, float, str, Bbox1, Link, QualifiedInputValue1) + ): + return value + elif isinstance(value, List): return value raise ValueError(f"Invalid type for Input: {type(value)}") diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_workflows.py b/app-cp/src/unity_sps_ogc_processes_api/models/input_workflows.py index 11a9420..65bb11b 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_workflows.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/input_workflows.py @@ -17,160 +17,114 @@ import json import pprint -import re # noqa: F401 -from typing import Dict, List, Optional, Union +from typing import Any, Dict, List, Union -from pydantic import BaseModel, ValidationError, field_validator -from typing_extensions import Literal +from pydantic import RootModel, ValidationError, model_validator -from unity_sps_ogc_processes_api.models.inline_or_ref_data_workflows import ( - InlineOrRefDataWorkflows, +from unity_sps_ogc_processes_api.models.bbox1 import Bbox1 +from unity_sps_ogc_processes_api.models.input_collection import InputCollection +from unity_sps_ogc_processes_api.models.input_parameterized import InputParameterized +from unity_sps_ogc_processes_api.models.input_process import InputProcess +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.qualified_input_value_workflows import ( + QualifiedInputValueWorkflows, ) -try: - from typing import Self -except ImportError: - from typing_extensions import Self - -INPUTWORKFLOWS_ONE_OF_SCHEMAS = [ - "InlineOrRefDataWorkflows", - "List[InlineOrRefDataWorkflows]", -] - - -class InputWorkflows(BaseModel): - """ - InputWorkflows - """ - - # data type: InlineOrRefDataWorkflows - oneof_schema_1_validator: Optional[InlineOrRefDataWorkflows] = None - # data type: List[InlineOrRefDataWorkflows] - oneof_schema_2_validator: Optional[List[InlineOrRefDataWorkflows]] = None - actual_instance: Optional[ - Union[InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]] - ] = None - one_of_schemas: List[str] = Literal[ - "InlineOrRefDataWorkflows", "List[InlineOrRefDataWorkflows]" + +class InputWorkflows(RootModel): + root: Union[ + Bbox1, + InputCollection, + InputParameterized, + InputProcess, + List[Any], + bool, + float, + int, + str, + Link, + QualifiedInputValueWorkflows, + List[ + Union[ + Bbox1, + InputCollection, + InputParameterized, + InputProcess, + List[Any], + bool, + float, + int, + str, + Link, + QualifiedInputValueWorkflows, + ] + ], ] - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) - if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator("actual_instance") - def actual_instance_must_validate_oneof(cls, v): - instance = InputWorkflows.model_construct() - error_messages = [] - match = 0 - # validate data type: InlineOrRefDataWorkflows - if not isinstance(v, InlineOrRefDataWorkflows): - error_messages.append( - f"Error! Input type `{type(v)}` is not `InlineOrRefDataWorkflows`" - ) - else: - match += 1 - # validate data type: List[InlineOrRefDataWorkflows] - try: - instance.oneof_schema_2_validator = v - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when setting `actual_instance` in InputWorkflows with oneOf schemas: InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when setting `actual_instance` in InputWorkflows with oneOf schemas: InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]. Details: " - + ", ".join(error_messages) - ) - else: - return v + @model_validator(mode="before") + @classmethod + def validate_type(cls, value): + if isinstance(value, dict): + for schema in [ + Bbox1, + InputCollection, + InputParameterized, + InputProcess, + Link, + QualifiedInputValueWorkflows, + ]: + try: + return schema(**value) + except ValidationError: + pass + elif isinstance(value, list): + return [cls.validate_type(item) for item in value] + elif isinstance( + value, + ( + bool, + int, + float, + str, + Bbox1, + InputCollection, + InputParameterized, + InputProcess, + Link, + QualifiedInputValueWorkflows, + ), + ): + return value + elif isinstance(value, List): + return value + raise ValueError(f"Invalid type for InputWorkflows: {type(value)}") @classmethod - def from_dict(cls, obj: dict) -> Self: - return cls.from_json(json.dumps(obj)) + def from_dict(cls, obj: Dict[str, Any]) -> InputWorkflows: + return cls(root=cls.validate_type(obj)) @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into InlineOrRefDataWorkflows - try: - instance.actual_instance = InlineOrRefDataWorkflows.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into List[InlineOrRefDataWorkflows] - try: - # validation - instance.oneof_schema_2_validator = json.loads(json_str) - # assign value to actual_instance - instance.actual_instance = instance.oneof_schema_2_validator - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when deserializing the JSON string into InputWorkflows with oneOf schemas: InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when deserializing the JSON string into InputWorkflows with oneOf schemas: InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]. Details: " - + ", ".join(error_messages) - ) - else: - return instance + def from_json(cls, json_str: str) -> InputWorkflows: + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + if isinstance(self.root, list): + return [self._item_to_dict(item) for item in self.root] + return self._item_to_dict(self.root) + + def _item_to_dict(self, item): + if hasattr(item, "model_dump"): + return item.model_dump() + return item def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - to_json = getattr(self.actual_instance, "to_json", None) - if callable(to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Dict: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - to_dict = getattr(self.actual_instance, "to_dict", None) - if callable(to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance + return json.dumps(self.to_dict()) + + def __getattr__(self, name: str) -> Any: + return getattr(self.root, name) + + def __repr__(self) -> str: + return f"InputWorkflows({self.root!r})" def to_str(self) -> str: - """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py b/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py index fa239fe..2448633 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py @@ -23,12 +23,16 @@ from unity_sps_ogc_processes_api.models.execution_unit import ExecutionUnit from unity_sps_ogc_processes_api.models.link import Link -from unity_sps_ogc_processes_api.models.ogcapppkg_array_inner import OgcapppkgArrayInner from unity_sps_ogc_processes_api.models.qualified_input_value import QualifiedInputValue class OgcapppkgExecutionUnit(RootModel): - root: Union[ExecutionUnit, Link, List[OgcapppkgArrayInner], QualifiedInputValue] + root: Union[ + ExecutionUnit, + Link, + QualifiedInputValue, + List[Union[ExecutionUnit, Link, QualifiedInputValue]], + ] @model_validator(mode="before") @classmethod @@ -39,17 +43,10 @@ def validate_type(cls, value): return schema(**value) except ValidationError: pass - try: - return [OgcapppkgArrayInner(**item) for item in value] - except ValidationError: - pass + elif isinstance(value, list): + return [cls.validate_type(item) for item in value] elif isinstance(value, (ExecutionUnit, Link, QualifiedInputValue)): return value - elif isinstance(value, list): - try: - return [OgcapppkgArrayInner(**item) for item in value] - except ValidationError: - pass raise ValueError(f"Invalid type for OgcapppkgExecutionUnit: {type(value)}") @classmethod From 5648b1ecdd19641ed709875fcd65de5a34f22eb1 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 15 Aug 2024 14:26:11 -0700 Subject: [PATCH 13/60] feat: Initial autogenerated FastAPI implementation --- .../openapi_server/impl/conformance_api.py | 13 ++++++++++-- .../apis/api_api.py | 15 +------------- .../apis/api_api_base.py | 2 +- .../apis/conformance_api.py | 20 ++++--------------- .../apis/conformance_api_base.py | 2 +- .../apis/dru_api.py | 16 +-------------- .../apis/dru_api_base.py | 2 +- .../apis/jobs_api.py | 16 ++------------- .../apis/jobs_api_base.py | 2 +- .../apis/landing_page_api.py | 16 +-------------- .../apis/landing_page_api_base.py | 2 +- .../apis/processes_api.py | 16 +-------------- .../apis/processes_api_base.py | 2 +- .../models/ogcapppkg.py | 7 ++----- 14 files changed, 29 insertions(+), 102 deletions(-) diff --git a/app-cp/src/openapi_server/impl/conformance_api.py b/app-cp/src/openapi_server/impl/conformance_api.py index 43a7d6d..13213ed 100644 --- a/app-cp/src/openapi_server/impl/conformance_api.py +++ b/app-cp/src/openapi_server/impl/conformance_api.py @@ -1,7 +1,16 @@ from unity_sps_ogc_processes_api.apis.conformance_api_base import BaseConformanceApi +from unity_sps_ogc_processes_api.models.conf_classes import ConfClasses class ConformanceApiImpl(BaseConformanceApi): def get_conformance(self): - # Implement get_conformance logic here - pass + return ConfClasses( + conforms_to=[ + "http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/core", + "http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/ogc-process-description", + "http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/json", + "http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/job-list", + "http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/callback", + "http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/dismiss", + ] + ) diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/api_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/api_api.py index 9813300..b40cbea 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/api_api.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/api_api.py @@ -2,21 +2,8 @@ import importlib import pkgutil -from typing import Dict, List # noqa: F401 -from fastapi import ( # noqa: F401 - APIRouter, - Body, - Cookie, - Depends, - Form, - Header, - Path, - Query, - Response, - Security, - status, -) +from fastapi import APIRouter, Query import openapi_server.impl from unity_sps_ogc_processes_api.models.enumeration import Enumeration diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/api_api_base.py b/app-cp/src/unity_sps_ogc_processes_api/apis/api_api_base.py index 225be12..dde5ebf 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/api_api_base.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/api_api_base.py @@ -1,6 +1,6 @@ # coding: utf-8 -from typing import ClassVar, Dict, List, Tuple # noqa: F401 +from typing import ClassVar, Tuple from unity_sps_ogc_processes_api.models.enumeration import Enumeration diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api.py index 8db14ff..2aa2bc7 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api.py @@ -2,26 +2,13 @@ import importlib import pkgutil -from typing import Dict, List # noqa: F401 -from fastapi import ( # noqa: F401 - APIRouter, - Body, - Cookie, - Depends, - Form, - Header, - Path, - Query, - Response, - Security, - status, -) +from fastapi import APIRouter, Query import openapi_server.impl +from unity_sps_ogc_processes_api.apis.conformance_api_base import BaseConformanceApi from unity_sps_ogc_processes_api.models.conf_classes import ConfClasses from unity_sps_ogc_processes_api.models.exception import Exception -from unity_sps_ogc_processes_api.models.extra_models import TokenModel # noqa: F401 router = APIRouter() @@ -53,4 +40,5 @@ async def get_conformance( description="The format of the response. If no value is provided, the accept header is used to determine the format. Accepted values are 'json' or 'html'.", alias="f", ), -) -> ConfClasses: ... +) -> ConfClasses: + return BaseConformanceApi.subclasses[0]().get_conformance() diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py b/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py index 7373dca..7b93c60 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py @@ -1,6 +1,6 @@ # coding: utf-8 -from typing import ClassVar, Dict, List, Tuple # noqa: F401 +from typing import ClassVar, Tuple from unity_sps_ogc_processes_api.models.conf_classes import ConfClasses diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py index abfd6cb..e2e7df9 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py @@ -2,26 +2,12 @@ import importlib import pkgutil -from typing import Dict, List # noqa: F401 -from fastapi import ( # noqa: F401 - APIRouter, - Body, - Cookie, - Depends, - Form, - Header, - Path, - Query, - Response, - Security, - status, -) +from fastapi import APIRouter, Body, Path, Query import openapi_server.impl from unity_sps_ogc_processes_api.apis.dru_api_base import BaseDRUApi from unity_sps_ogc_processes_api.models.exception import Exception -from unity_sps_ogc_processes_api.models.extra_models import TokenModel # noqa: F401 from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg router = APIRouter() diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api_base.py b/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api_base.py index c84af42..6e60ccf 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api_base.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api_base.py @@ -1,6 +1,6 @@ # coding: utf-8 -from typing import ClassVar, Dict, List, Tuple # noqa: F401 +from typing import ClassVar, Tuple from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api.py index d20d04b..98c8c5f 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api.py @@ -2,21 +2,9 @@ import importlib import pkgutil -from typing import Dict, List # noqa: F401 +from typing import Dict -from fastapi import ( # noqa: F401 - APIRouter, - Body, - Cookie, - Depends, - Form, - Header, - Path, - Query, - Response, - Security, - status, -) +from fastapi import APIRouter, Header, Path import openapi_server.impl from unity_sps_ogc_processes_api.apis.jobs_api_base import BaseJobsApi diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py b/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py index acfe79d..ec4bd3a 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py @@ -1,6 +1,6 @@ # coding: utf-8 -from typing import ClassVar, Dict, List, Tuple # noqa: F401 +from typing import ClassVar, Dict, Tuple from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData from unity_sps_ogc_processes_api.models.job_list import JobList diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api.py index 7bafb52..e60014a 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api.py @@ -2,25 +2,11 @@ import importlib import pkgutil -from typing import Dict, List # noqa: F401 -from fastapi import ( # noqa: F401 - APIRouter, - Body, - Cookie, - Depends, - Form, - Header, - Path, - Query, - Response, - Security, - status, -) +from fastapi import APIRouter, Query import openapi_server.impl from unity_sps_ogc_processes_api.models.exception import Exception -from unity_sps_ogc_processes_api.models.extra_models import TokenModel # noqa: F401 from unity_sps_ogc_processes_api.models.landing_page import LandingPage router = APIRouter() diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api_base.py b/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api_base.py index d389c11..ddceacd 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api_base.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api_base.py @@ -1,6 +1,6 @@ # coding: utf-8 -from typing import ClassVar, Dict, List, Tuple # noqa: F401 +from typing import ClassVar, Tuple from unity_sps_ogc_processes_api.models.landing_page import LandingPage diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api.py index 3bddd1a..b06465a 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api.py @@ -2,28 +2,14 @@ import importlib import pkgutil -from typing import Dict, List # noqa: F401 -from fastapi import ( # noqa: F401 - APIRouter, - Body, - Cookie, - Depends, - Form, - Header, - Path, - Query, - Response, - Security, - status, -) +from fastapi import APIRouter, Body, Header, Path, Query import openapi_server.impl from unity_sps_ogc_processes_api.apis.processes_api_base import BaseProcessesApi from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows -from unity_sps_ogc_processes_api.models.extra_models import TokenModel # noqa: F401 from unity_sps_ogc_processes_api.models.process import Process from unity_sps_ogc_processes_api.models.process_list import ProcessList from unity_sps_ogc_processes_api.models.status_info import StatusInfo diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api_base.py b/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api_base.py index 6230461..d4b8adc 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api_base.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api_base.py @@ -1,6 +1,6 @@ # coding: utf-8 -from typing import ClassVar, Dict, List, Tuple # noqa: F401 +from typing import ClassVar, Tuple from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg.py b/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg.py index 5a5f97f..5c5d2c3 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg.py @@ -17,8 +17,7 @@ import json import pprint -import re # noqa: F401 -from typing import Any, ClassVar, Dict, List, Optional +from typing import Any, ClassVar, Dict, List from pydantic import BaseModel, Field @@ -38,9 +37,7 @@ class Ogcapppkg(BaseModel): Ogcapppkg """ # noqa: E501 - process_description: Optional[Process] = Field( - default=None, alias="processDescription" - ) + process_description: Process = Field(alias="processDescription") execution_unit: OgcapppkgExecutionUnit = Field(alias="executionUnit") __properties: ClassVar[List[str]] = ["processDescription", "executionUnit"] From 466cf1e9b317d292e4be4a6d76d791accdcd656f Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 15 Aug 2024 14:27:31 -0700 Subject: [PATCH 14/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/api_api.py | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 app-cp/src/openapi_server/api_api.py diff --git a/app-cp/src/openapi_server/api_api.py b/app-cp/src/openapi_server/api_api.py deleted file mode 100644 index 7b47ce0..0000000 --- a/app-cp/src/openapi_server/api_api.py +++ /dev/null @@ -1,7 +0,0 @@ -from unity_sps_ogc_processes_api.apis.api_api_base import BaseAPIApi - - -class APIApiImpl(BaseAPIApi): - def get_api(self): - # Implement get_api logic here - pass From 51f680f54a9f8fa8d25a6bacb6b0eae13773af1a Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 15 Aug 2024 14:27:38 -0700 Subject: [PATCH 15/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/api_api.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 app-cp/src/openapi_server/impl/api_api.py diff --git a/app-cp/src/openapi_server/impl/api_api.py b/app-cp/src/openapi_server/impl/api_api.py new file mode 100644 index 0000000..7b47ce0 --- /dev/null +++ b/app-cp/src/openapi_server/impl/api_api.py @@ -0,0 +1,7 @@ +from unity_sps_ogc_processes_api.apis.api_api_base import BaseAPIApi + + +class APIApiImpl(BaseAPIApi): + def get_api(self): + # Implement get_api logic here + pass From 6d4d7fe0d99fedc87ba25bf8ebd4235f2ff37db0 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Mon, 19 Aug 2024 13:49:59 -0700 Subject: [PATCH 16/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/api_api.py | 11 +- app-cp/src/openapi_server/impl/dru_api.py | 48 ++- app-cp/src/openapi_server/impl/jobs_api.py | 71 +++- .../openapi_server/impl/landing_page_api.py | 41 ++- .../src/openapi_server/impl/processes_api.py | 83 ++++- .../apis/api_api.py | 7 +- .../apis/landing_page_api.py | 4 +- .../models/execute200_response.py | 316 +++--------------- 8 files changed, 275 insertions(+), 306 deletions(-) diff --git a/app-cp/src/openapi_server/impl/api_api.py b/app-cp/src/openapi_server/impl/api_api.py index 7b47ce0..273de80 100644 --- a/app-cp/src/openapi_server/impl/api_api.py +++ b/app-cp/src/openapi_server/impl/api_api.py @@ -1,7 +1,14 @@ +from fastapi.openapi.utils import get_openapi + from unity_sps_ogc_processes_api.apis.api_api_base import BaseAPIApi class APIApiImpl(BaseAPIApi): def get_api(self): - # Implement get_api logic here - pass + # Placeholder implementation + return get_openapi( + title="OGC API - Processes", + version="1.0.0", + description="This is a sample OpenAPI schema for OGC API - Processes", + routes=[], + ) diff --git a/app-cp/src/openapi_server/impl/dru_api.py b/app-cp/src/openapi_server/impl/dru_api.py index 16d7ae5..0be759d 100644 --- a/app-cp/src/openapi_server/impl/dru_api.py +++ b/app-cp/src/openapi_server/impl/dru_api.py @@ -1,4 +1,4 @@ -from fastapi import Response, status +from fastapi import HTTPException, Response, status from unity_sps_ogc_processes_api.apis.dru_api_base import BaseDRUApi from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg @@ -6,15 +6,47 @@ class DRUApiImpl(BaseDRUApi): def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: - # Implement deploy logic here + # Simulate deployment process + try: + # Here you would typically: + # 1. Validate the ogcapppkg + # 2. Extract and store the package + # 3. Register the new process - # Return a successful response with status code 201 - return Response(status_code=status.HTTP_201_CREATED) + # For now, we'll just create a dummy process + new_process = ogcapppkg.process_description + + # In a real implementation, you'd save this process to a database + + return Response( + status_code=status.HTTP_201_CREATED, + content=f"Process {new_process.id} deployed successfully", + ) + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: - # Implement replace logic here - pass + # Simulate replacement process + try: + # Here you would typically: + # 1. Check if the process exists + # 2. Validate the new ogcapppkg + # 3. Update the existing process with new data + + # For now, we'll just print a message + print(f"Process {processId} replaced with new package: {ogcapppkg.id}") + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) def undeploy(self, processId: str) -> None: - # Implement undeploy logic here - pass + # Simulate undeployment process + try: + # Here you would typically: + # 1. Check if the process exists + # 2. Remove the process from the system + # 3. Clean up any associated resources + + # For now, we'll just print a message + print(f"Process {processId} undeployed successfully") + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) diff --git a/app-cp/src/openapi_server/impl/jobs_api.py b/app-cp/src/openapi_server/impl/jobs_api.py index 25fab14..9f07475 100644 --- a/app-cp/src/openapi_server/impl/jobs_api.py +++ b/app-cp/src/openapi_server/impl/jobs_api.py @@ -1,15 +1,68 @@ +from datetime import datetime +from typing import Dict + from unity_sps_ogc_processes_api.apis.jobs_api_base import BaseJobsApi +from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData +from unity_sps_ogc_processes_api.models.job_list import JobList +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.status_code import StatusCode +from unity_sps_ogc_processes_api.models.status_info import StatusInfo class JobsApiImpl(BaseJobsApi): - def get_job(self, jobId): - # Implement get_job logic here - pass + def dismiss(self, jobId: str) -> StatusInfo: + return StatusInfo( + type="process", + job_id=jobId, + status=StatusCode.DISMISSED, + message="Job dismissed", + updated=datetime.now(), + ) + + def get_jobs(self) -> JobList: + return JobList( + jobs=[ + StatusInfo( + type="process", + job_id="job1", + status=StatusCode.RUNNING, + created=datetime.now(), + updated=datetime.now(), + ), + StatusInfo( + type="process", + job_id="job2", + status=StatusCode.SUCCESSFUL, + created=datetime.now(), + started=datetime.now(), + finished=datetime.now(), + updated=datetime.now(), + ), + ], + links=[ + Link( + href="http://example.com/api/jobs", + rel="self", + type="application/json", + title="this document", + ) + ], + ) - def get_jobs(self): - # Implement get_jobs logic here - pass + def get_result(self, jobId: str, prefer: str) -> Dict[str, InlineOrRefData]: + return { + "output1": InlineOrRefData(href="http://example.com/result1"), + "output2": InlineOrRefData(href="http://example.com/result2"), + } - def post_job(self, execute): - # Implement post_job logic here - pass + def get_status(self, jobId: str) -> StatusInfo: + return StatusInfo( + type="process", + job_id=jobId, + status=StatusCode.SUCCESSFUL, + created=datetime.now(), + started=datetime.now(), + finished=datetime.now(), + updated=datetime.now(), + progress=100, + ) diff --git a/app-cp/src/openapi_server/impl/landing_page_api.py b/app-cp/src/openapi_server/impl/landing_page_api.py index d60c820..721d2d6 100644 --- a/app-cp/src/openapi_server/impl/landing_page_api.py +++ b/app-cp/src/openapi_server/impl/landing_page_api.py @@ -1,7 +1,42 @@ from unity_sps_ogc_processes_api.apis.landing_page_api_base import BaseLandingPageApi +from unity_sps_ogc_processes_api.models.landing_page import LandingPage +from unity_sps_ogc_processes_api.models.link import Link class LandingPageApiImpl(BaseLandingPageApi): - def get_landing_page(self): - # Implement get_landing_page logic here - pass + def get_landing_page(self, f: str = None) -> LandingPage: + landing_page = LandingPage( + title="OGC API - Processes", + description="This is the landing page for the OGC API - Processes implementation.", + links=[ + Link( + href="/", rel="self", type="application/json", title="This document" + ), + Link( + href="/api", # Assuming the API definition is at /api + rel="service-desc", + type="application/openapi+json;version=3.0", + title="The API definition", + ), + Link( + href="/conformance", + rel="http://www.opengis.net/def/rel/ogc/1.0/conformance", + type="application/json", + title="Conformance declaration", + ), + Link( + href="/processes", + rel="http://www.opengis.net/def/rel/ogc/1.0/processes", + type="application/json", + title="Processes metadata", + ), + Link( + href="/jobs", + rel="http://www.opengis.net/def/rel/ogc/1.0/job-list", + type="application/json", + title="Job monitoring", + ), + ], + ) + + return landing_page diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index 870ee03..fb09024 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -1,11 +1,82 @@ from unity_sps_ogc_processes_api.apis.processes_api_base import BaseProcessesApi +from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response +from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.process import Process +from unity_sps_ogc_processes_api.models.process_list import ProcessList +from unity_sps_ogc_processes_api.models.process_summary import ProcessSummary +from unity_sps_ogc_processes_api.models.status_info import StatusInfo class ProcessesApiImpl(BaseProcessesApi): - def get_process(self, processId): - # Implement get_process logic here - pass + def get_process_description(self, processId: str) -> Process: + return Process( + id=processId, + title="Sample Process", + description="This is a sample process description", + version="1.0.0", + jobControlOptions=["sync-execute", "async-execute"], + outputTransmission=["value"], + executeEndpoint=f"http://example.com/api/processes/{processId}/execution", + inputs={}, + outputs={}, + links=[ + Link( + href=f"http://example.com/api/processes/{processId}", + rel="self", + type="application/json", + title="This process", + ) + ], + ) - def get_processes(self): - # Implement get_processes logic here - pass + def get_processes(self) -> ProcessList: + return ProcessList( + processes=[ + ProcessSummary( + id="process1", + title="Process 1", + description="Description 1", + version="1.0.0", + ), + ProcessSummary( + id="process2", + title="Process 2", + description="Description 2", + version="1.0.0", + ), + ], + links=[ + Link( + href="http://example.com/api/processes", + rel="self", + type="application/json", + title="this document", + ) + ], + ) + + def execute( + self, + processId: str, + execute_workflows: ExecuteWorkflows, + response: str, + prefer: str, + ) -> Execute200Response: + # Placeholder implementation + # In a real-world scenario, you would execute the process here + # and return the appropriate response based on the execution mode + + if prefer == "respond-async": + # Asynchronous execution + return StatusInfo( + type="process", + job_id="sample_job_id", + status="accepted", + message="Process execution started asynchronously", + ) + else: + # Synchronous execution + return Execute200Response( + outputs={"result": "Sample output for synchronous execution"} + ) diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/api_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/api_api.py index b40cbea..1bbdbdb 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/api_api.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/api_api.py @@ -6,6 +6,7 @@ from fastapi import APIRouter, Query import openapi_server.impl +from unity_sps_ogc_processes_api.apis.api_api_base import BaseAPIApi from unity_sps_ogc_processes_api.models.enumeration import Enumeration from unity_sps_ogc_processes_api.models.exception import Exception @@ -36,7 +37,8 @@ async def get_api( description="The format of the response. If no value is provided, the accept header is used to determine the format. Accepted values are 'json' or 'html'.", alias="f", ), -) -> object: ... +) -> object: + return BaseAPIApi.subclasses[0]().get_api(f) @router.get( @@ -66,4 +68,5 @@ async def get_api_processes( description="The format of the response. If no value is provided, the accept header is used to determine the format. Accepted values are 'json' or 'html'.", alias="f", ), -) -> Enumeration: ... +) -> Enumeration: + return BaseAPIApi.subclasses[0]().get_api_processes(f) diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api.py index e60014a..b1e8e2c 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api.py @@ -6,6 +6,7 @@ from fastapi import APIRouter, Query import openapi_server.impl +from unity_sps_ogc_processes_api.apis.landing_page_api_base import BaseLandingPageApi from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.landing_page import LandingPage @@ -39,4 +40,5 @@ async def get_landing_page( description="The format of the response. If no value is provided, the accept header is used to determine the format. Accepted values are 'json' or 'html'.", alias="f", ), -) -> LandingPage: ... +) -> LandingPage: + return BaseLandingPageApi.subclasses[0]().get_landing_page(f) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/execute200_response.py b/app-cp/src/unity_sps_ogc_processes_api/models/execute200_response.py index ad9a1ce..77f0081 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/execute200_response.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/execute200_response.py @@ -15,296 +15,62 @@ from __future__ import annotations -import json -import pprint import re # noqa: F401 -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, List, Union -from pydantic import ( - BaseModel, - FilePath, - StrictBool, - StrictBytes, - StrictFloat, - StrictInt, - StrictStr, - ValidationError, - field_validator, -) -from typing_extensions import Literal +from pydantic import FilePath, RootModel, field_validator -from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData +from unity_sps_ogc_processes_api.models.bbox import Bbox +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.qualified_input_value import QualifiedInputValue try: from typing import Self except ImportError: from typing_extensions import Self -EXECUTE200RESPONSE_ONE_OF_SCHEMAS = [ - "Dict[str, InlineOrRefData]", - "List[object]", - "bool", - "FilePath", - "float", - "int", - "object", - "str", -] - -class Execute200Response(BaseModel): - """ - Execute200Response - """ - - # data type: str - oneof_schema_1_validator: Optional[StrictStr] = None - # data type: float - oneof_schema_2_validator: Optional[Union[StrictFloat, StrictInt]] = None - # data type: int - oneof_schema_3_validator: Optional[StrictInt] = None - # data type: object - oneof_schema_4_validator: Optional[Dict[str, Any]] = None - # data type: List[object] - oneof_schema_5_validator: Optional[List[Dict[str, Any]]] = None - # data type: bool - oneof_schema_6_validator: Optional[StrictBool] = None - # data type: FilePath - oneof_schema_7_validator: Optional[Union[StrictBytes, StrictStr]] = None - # data type: Dict[str, InlineOrRefData] - oneof_schema_8_validator: Optional[Dict[str, InlineOrRefData]] = None - actual_instance: Optional[ - Union[ - Dict[str, InlineOrRefData], - List[object], - bool, - FilePath, - float, - int, - object, +class Execute200Response(RootModel): + root: Union[ + Dict[ str, - ] - ] = None - one_of_schemas: List[str] = Literal[ - "Dict[str, InlineOrRefData]", - "List[object]", - "bool", - "FilePath", - "float", - "int", - "object", - "str", + Union[Bbox, List[Any], bool, float, int, str, Link, QualifiedInputValue], + ], + List[Any], + bool, + FilePath, + float, + int, + Dict[str, Any], + str, ] - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) - if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator("actual_instance") - def actual_instance_must_validate_oneof(cls, v): - instance = Execute200Response.model_construct() - error_messages = [] - match = 0 - # validate data type: str - try: - instance.oneof_schema_1_validator = v - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # validate data type: float - try: - instance.oneof_schema_2_validator = v - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # validate data type: int - try: - instance.oneof_schema_3_validator = v - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # validate data type: object - try: - instance.oneof_schema_4_validator = v - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # validate data type: List[object] - try: - instance.oneof_schema_5_validator = v - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # validate data type: bool - try: - instance.oneof_schema_6_validator = v - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # validate data type: FilePath - try: - instance.oneof_schema_7_validator = v - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # validate data type: Dict[str, InlineOrRefData] - try: - instance.oneof_schema_8_validator = v - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when setting `actual_instance` in Execute200Response with oneOf schemas: Dict[str, InlineOrRefData], List[object], bool, FilePath, float, int, object, str. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when setting `actual_instance` in Execute200Response with oneOf schemas: Dict[str, InlineOrRefData], List[object], bool, FilePath, float, int, object, str. Details: " - + ", ".join(error_messages) - ) - else: - return v - - @classmethod - def from_dict(cls, obj: dict) -> Self: - return cls.from_json(json.dumps(obj)) + @field_validator("root") + def validate_root(cls, v): + if isinstance(v, dict): + for key, value in v.items(): + if not isinstance(key, str): + raise ValueError( + f"Dictionary keys must be strings, got {type(key)}" + ) + if not isinstance( + value, + (Bbox, list, bool, float, int, str, Link, QualifiedInputValue), + ): + raise ValueError(f"Invalid value type for key {key}: {type(value)}") + return v @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into str - try: - # validation - instance.oneof_schema_1_validator = json.loads(json_str) - # assign value to actual_instance - instance.actual_instance = instance.oneof_schema_1_validator - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into float - try: - # validation - instance.oneof_schema_2_validator = json.loads(json_str) - # assign value to actual_instance - instance.actual_instance = instance.oneof_schema_2_validator - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into int - try: - # validation - instance.oneof_schema_3_validator = json.loads(json_str) - # assign value to actual_instance - instance.actual_instance = instance.oneof_schema_3_validator - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into object - try: - # validation - instance.oneof_schema_4_validator = json.loads(json_str) - # assign value to actual_instance - instance.actual_instance = instance.oneof_schema_4_validator - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into List[object] - try: - # validation - instance.oneof_schema_5_validator = json.loads(json_str) - # assign value to actual_instance - instance.actual_instance = instance.oneof_schema_5_validator - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into bool - try: - # validation - instance.oneof_schema_6_validator = json.loads(json_str) - # assign value to actual_instance - instance.actual_instance = instance.oneof_schema_6_validator - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into FilePath - try: - # validation - instance.oneof_schema_7_validator = json.loads(json_str) - # assign value to actual_instance - instance.actual_instance = instance.oneof_schema_7_validator - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into Dict[str, InlineOrRefData] - try: - # validation - instance.oneof_schema_8_validator = json.loads(json_str) - # assign value to actual_instance - instance.actual_instance = instance.oneof_schema_8_validator - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when deserializing the JSON string into Execute200Response with oneOf schemas: Dict[str, InlineOrRefData], List[object], bool, FilePath, float, int, object, str. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when deserializing the JSON string into Execute200Response with oneOf schemas: Dict[str, InlineOrRefData], List[object], bool, FilePath, float, int, object, str. Details: " - + ", ".join(error_messages) - ) - else: - return instance - - def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - to_json = getattr(self.actual_instance, "to_json", None) - if callable(to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) + def from_dict(cls, obj: Dict[str, Any]) -> Self: + return cls(root=obj) - def to_dict(self) -> Dict: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None + def to_dict(self) -> Dict[str, Any]: + return self.root if isinstance(self.root, dict) else {"value": self.root} - to_dict = getattr(self.actual_instance, "to_dict", None) - if callable(to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance + def __getattr__(self, name: str) -> Any: + if isinstance(self.root, dict): + return self.root.get(name) + raise AttributeError(f"'Execute200Response' object has no attribute '{name}'") - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.model_dump()) + def __repr__(self) -> str: + return f"Execute200Response({self.root!r})" From 5e01259c58dfccccd601b7cf4dfa3e2071093f9e Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Wed, 28 Aug 2024 11:14:26 -0700 Subject: [PATCH 17/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/api_api.py | 19 +- .../models/inline_or_ref_data.py | 192 ++++-------------- 2 files changed, 56 insertions(+), 155 deletions(-) diff --git a/app-cp/src/openapi_server/impl/api_api.py b/app-cp/src/openapi_server/impl/api_api.py index 273de80..998b854 100644 --- a/app-cp/src/openapi_server/impl/api_api.py +++ b/app-cp/src/openapi_server/impl/api_api.py @@ -1,14 +1,25 @@ from fastapi.openapi.utils import get_openapi from unity_sps_ogc_processes_api.apis.api_api_base import BaseAPIApi +from unity_sps_ogc_processes_api.models.enumeration import Enumeration class APIApiImpl(BaseAPIApi): - def get_api(self): - # Placeholder implementation + def get_api(self, f: str = None): + # Implementation for retrieving the API definition return get_openapi( title="OGC API - Processes", version="1.0.0", - description="This is a sample OpenAPI schema for OGC API - Processes", - routes=[], + description="This is the OpenAPI definition for OGC API - Processes", + routes=[], # You may need to populate this with actual routes + ) + + def get_api_processes(self, f: str = None) -> Enumeration: + # Implementation for retrieving the list of available processes + # This is a placeholder implementation. You should replace this with actual process list. + return Enumeration( + type="enum", # Changed from 'array' to 'enum' + enum=["process1", "process2", "process3"], # Example process names + title="Available Processes", + description="List of processes available in this API implementation", ) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py b/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py index 4711b4b..2bba538 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py +++ b/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py @@ -17,168 +17,58 @@ import json import pprint -import re # noqa: F401 -from typing import Dict, List, Optional, Union +from typing import Any, Dict, List, Union -from pydantic import BaseModel, ValidationError, field_validator -from typing_extensions import Literal +from pydantic import RootModel, model_validator -from unity_sps_ogc_processes_api.models.input_value_no_object import InputValueNoObject +from unity_sps_ogc_processes_api.models.bbox import Bbox from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.qualified_input_value import QualifiedInputValue -try: - from typing import Self -except ImportError: - from typing_extensions import Self - -INLINEORREFDATA_ONE_OF_SCHEMAS = ["InputValueNoObject", "Link", "QualifiedInputValue"] - - -class InlineOrRefData(BaseModel): - """ - InlineOrRefData - """ - - # data type: InputValueNoObject - oneof_schema_1_validator: Optional[InputValueNoObject] = None - # data type: QualifiedInputValue - oneof_schema_2_validator: Optional[QualifiedInputValue] = None - # data type: Link - oneof_schema_3_validator: Optional[Link] = None - actual_instance: Optional[Union[InputValueNoObject, Link, QualifiedInputValue]] = ( - None - ) - one_of_schemas: List[str] = Literal[ - "InputValueNoObject", "Link", "QualifiedInputValue" - ] - - model_config = { - "validate_assignment": True, - "protected_namespaces": (), - } - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) - if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator("actual_instance") - def actual_instance_must_validate_oneof(cls, v): - InlineOrRefData.model_construct() - error_messages = [] - match = 0 - # validate data type: InputValueNoObject - if not isinstance(v, InputValueNoObject): - error_messages.append( - f"Error! Input type `{type(v)}` is not `InputValueNoObject`" - ) - else: - match += 1 - # validate data type: QualifiedInputValue - if not isinstance(v, QualifiedInputValue): - error_messages.append( - f"Error! Input type `{type(v)}` is not `QualifiedInputValue`" - ) - else: - match += 1 - # validate data type: Link - if not isinstance(v, Link): - error_messages.append(f"Error! Input type `{type(v)}` is not `Link`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when setting `actual_instance` in InlineOrRefData with oneOf schemas: InputValueNoObject, Link, QualifiedInputValue. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when setting `actual_instance` in InlineOrRefData with oneOf schemas: InputValueNoObject, Link, QualifiedInputValue. Details: " - + ", ".join(error_messages) - ) - else: - return v + +class InlineOrRefData(RootModel): + root: Union[Bbox, List[Any], bool, float, int, str, Link, QualifiedInputValue] + + @model_validator(mode="before") + @classmethod + def validate_type(cls, value): + if isinstance(value, dict): + if all(k in value for k in ["mediaType", "encoding", "schema", "value"]): + return QualifiedInputValue(**value) + if "href" in value: + return Link(**value) + if "bbox" in value: + return Bbox(**value) + return value # Handle other dict cases + elif isinstance( + value, (Bbox, Link, QualifiedInputValue, bool, int, float, str) + ): + return value + elif isinstance(value, list): + return value + raise ValueError(f"Invalid type for InlineOrRefData: {type(value)}") @classmethod - def from_dict(cls, obj: dict) -> Self: - return cls.from_json(json.dumps(obj)) + def from_dict(cls, obj: Dict[str, Any]) -> InlineOrRefData: + return cls(root=cls.validate_type(obj)) @classmethod - def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into InputValueNoObject - try: - instance.actual_instance = InputValueNoObject.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into QualifiedInputValue - try: - instance.actual_instance = QualifiedInputValue.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into Link - try: - instance.actual_instance = Link.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError( - "Multiple matches found when deserializing the JSON string into InlineOrRefData with oneOf schemas: InputValueNoObject, Link, QualifiedInputValue. Details: " - + ", ".join(error_messages) - ) - elif match == 0: - # no match - raise ValueError( - "No match found when deserializing the JSON string into InlineOrRefData with oneOf schemas: InputValueNoObject, Link, QualifiedInputValue. Details: " - + ", ".join(error_messages) - ) - else: - return instance + def from_json(cls, json_str: str) -> InlineOrRefData: + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + if isinstance(self.root, (Bbox, Link, QualifiedInputValue)): + return self.root.model_dump() + return self.root def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - to_json = getattr(self.actual_instance, "to_json", None) - if callable(to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Dict: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - to_dict = getattr(self.actual_instance, "to_dict", None) - if callable(to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance + return json.dumps(self.to_dict()) + + def __getattr__(self, name: str) -> Any: + return getattr(self.root, name) + + def __repr__(self) -> str: + return f"InlineOrRefData({self.root!r})" def to_str(self) -> str: - """Returns the string representation of the actual instance""" return pprint.pformat(self.model_dump()) From d44abf2b7caa349e52eaa210e1ee737a02888ffe Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 29 Aug 2024 12:36:23 -0700 Subject: [PATCH 18/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/config/__init__.py | 0 app-cp/src/openapi_server/config/config.py | 13 + .../src/openapi_server/database/__init__.py | 13 + app-cp/src/openapi_server/database/crud.py | 27 +- app-cp/src/openapi_server/database/models.py | 32 +- app-cp/src/openapi_server/impl/dru_api.py | 212 +++++++- app-cp/src/openapi_server/utils/__init__.py | 0 app-cp/src/openapi_server/utils/redis.py | 25 + .../unity_sps_ogc_processes_api/__init__.py | 0 .../apis/dru_api.py | 28 +- .../dependencies.py | 24 + .../src/unity_sps_ogc_processes_api/main.py | 19 +- app-cp/tests/conftest.py | 507 +++++++++++++++++- app-cp/tests/test_dru_api.py | 175 ++---- 14 files changed, 900 insertions(+), 175 deletions(-) create mode 100644 app-cp/src/openapi_server/config/__init__.py create mode 100644 app-cp/src/openapi_server/config/config.py create mode 100644 app-cp/src/openapi_server/database/__init__.py create mode 100644 app-cp/src/openapi_server/utils/__init__.py create mode 100644 app-cp/src/openapi_server/utils/redis.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/__init__.py create mode 100644 app-cp/src/unity_sps_ogc_processes_api/dependencies.py diff --git a/app-cp/src/openapi_server/config/__init__.py b/app-cp/src/openapi_server/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app-cp/src/openapi_server/config/config.py b/app-cp/src/openapi_server/config/config.py new file mode 100644 index 0000000..3d42716 --- /dev/null +++ b/app-cp/src/openapi_server/config/config.py @@ -0,0 +1,13 @@ +from pydantic import HttpUrl, SecretStr +from pydantic_settings import BaseSettings + + +class Settings(BaseSettings): + DB_URL: str = "sqlite:///:memory:" + REDIS_HOST: str = "http://localhost" + REDIS_PORT: int = 6379 + EMS_API_URL: HttpUrl = "http://localhost:8080/api/v1" + EMS_API_AUTH_USERNAME: str = "username" + EMS_API_AUTH_PASSWORD: SecretStr = "password" + DAG_CATALOG_DIRECTORY: str = "/dag-catalog" + DEPLOYED_DAGS_DIRECTORY: str = "/deployed-dags" diff --git a/app-cp/src/openapi_server/database/__init__.py b/app-cp/src/openapi_server/database/__init__.py new file mode 100644 index 0000000..30d2541 --- /dev/null +++ b/app-cp/src/openapi_server/database/__init__.py @@ -0,0 +1,13 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import declarative_base, sessionmaker + +from ..config import config + +settings = config.Settings() + +SQLALCHEMY_DATABASE_URL = settings.DB_URL + +engine = create_engine(SQLALCHEMY_DATABASE_URL) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +Base = declarative_base() diff --git a/app-cp/src/openapi_server/database/crud.py b/app-cp/src/openapi_server/database/crud.py index f316c16..e88e249 100644 --- a/app-cp/src/openapi_server/database/crud.py +++ b/app-cp/src/openapi_server/database/crud.py @@ -1,13 +1,27 @@ from sqlalchemy.orm import Session -from unity_sps_ogc_processes_api.models import ogcapppkg +from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg from . import models -def create_process(db: Session, process: ogcapppkg.Ogcapppkg): - db_process = models.Process(**process.dict()) - db.add(db_process) +def create_process(db: Session, ogcapppkg: Ogcapppkg): + db_process = models.Process(**ogcapppkg.process_description.model_dump()) + db_execution_unit = models.ExecutionUnit(**ogcapppkg.execution_unit.model_dump()) + db_ogcapppkg = models.Ogcapppkg( + process=db_process, execution_unit=db_execution_unit + ) + db.add(db_ogcapppkg) + db.commit() + db.refresh(db_ogcapppkg) + return db_ogcapppkg + + +def update_process(db: Session, process_id: str, process_data: dict): + db_process = db.query(models.Process).filter(models.Process.id == process_id).one() + for key, value in process_data.items(): + if hasattr(db_process, key): + setattr(db_process, key, value) db.commit() db.refresh(db_process) return db_process @@ -21,8 +35,9 @@ def get_process(db: Session, process_id: str): return db.query(models.Process).filter(models.Process.id == process_id).one() -def delete_process(db: Session, process: models.Process): - db.delete(process) +def delete_process(db: Session, process_id: str): + db_process = db.query(models.Process).filter(models.Process.id == process_id).one() + db.delete(db_process) db.commit() diff --git a/app-cp/src/openapi_server/database/models.py b/app-cp/src/openapi_server/database/models.py index a1a2308..f6b8e36 100644 --- a/app-cp/src/openapi_server/database/models.py +++ b/app-cp/src/openapi_server/database/models.py @@ -1,27 +1,49 @@ from sqlalchemy import JSON, Column, DateTime, ForeignKey, Integer, String -from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship from sqlalchemy.sql import func -Base = declarative_base() +from . import Base class Process(Base): __tablename__ = "processes" _id = Column(Integer, primary_key=True) id = Column(String, index=True, unique=True, nullable=False) + version = Column(String) title = Column(String) description = Column(String) keywords = Column(JSON) - version = Column(String) - jobControlOptions = Column(JSON) + job_control_options = Column(JSON) links = Column(JSON) inputs = Column(JSON) outputs = Column(JSON) - metadata = Column(JSON) + deployment_status = Column(String, default="pending") jobs = relationship("Job", back_populates="process") +# https://docs.sqlalchemy.org/en/20/orm/declarative_tables.html#appending-additional-columns-to-an-existing-declarative-mapped-class +Process.metadata = Column("metadata", JSON) + + +class ExecutionUnit(Base): + __tablename__ = "execution_units" + _id = Column(Integer, primary_key=True) + type = Column(String) + image = Column(String) + deployment = Column(String) + config = Column(JSON) + additional_properties = Column(JSON) + ogcapppkg_id = Column(Integer, ForeignKey("ogcapppkgs._id")) + + +class Ogcapppkg(Base): + __tablename__ = "ogcapppkgs" + _id = Column(Integer, primary_key=True) + process_id = Column(String, ForeignKey("processes.id")) + process = relationship("Process", backref="ogcapppkg") + execution_unit = relationship("ExecutionUnit", uselist=False, backref="ogcapppkg") + + class Job(Base): __tablename__ = "jobs" _id = Column(Integer, primary_key=True) diff --git a/app-cp/src/openapi_server/impl/dru_api.py b/app-cp/src/openapi_server/impl/dru_api.py index 0be759d..25abe90 100644 --- a/app-cp/src/openapi_server/impl/dru_api.py +++ b/app-cp/src/openapi_server/impl/dru_api.py @@ -1,52 +1,218 @@ +import os +import shutil +import time + +import requests from fastapi import HTTPException, Response, status +from requests.auth import HTTPBasicAuth +from sqlalchemy.orm import Session +from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound +from openapi_server.config.config import Settings +from openapi_server.database import crud +from openapi_server.utils.redis import RedisLock from unity_sps_ogc_processes_api.apis.dru_api_base import BaseDRUApi from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg +def check_process_integrity(db: Session, process_id: str, new_process: bool): + process = None + try: + process = crud.get_process(db, process_id) + if new_process: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail=f"Process with ID '{process_id}' already exists", + ) + # TODO: Check if deployment_status is complete + # If not, raise an exception that its deployment status is not complete + except NoResultFound: + if not new_process: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Process with ID '{process_id}' not found", + ) + except MultipleResultsFound: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Multiple processes found with same ID '{process_id}', data integrity error", + ) + return process + + class DRUApiImpl(BaseDRUApi): + def __init__( + self, settings: Settings, redis_locking_client: RedisLock, db: Session + ): + self.settings = settings + self.redis_locking_client = redis_locking_client + self.db = db + def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: - # Simulate deployment process + check_process_integrity( + self.db, ogcapppkg.process_description.id, new_process=True + ) try: - # Here you would typically: - # 1. Validate the ogcapppkg - # 2. Extract and store the package - # 3. Register the new process + with self.redis_locking_client.lock( + "deploy_process_" + ogcapppkg.process_description.id + ): + # ogcapppkg.process_description.deployment_status = "deploying" + crud.create_process(self.db, ogcapppkg) + + dag_filename = ogcapppkg.process_description.id + ".py" + dag_catalog_filepath = os.path.join( + self.settings.DAG_CATALOG_DIRECTORY, dag_filename + ) + if not os.path.isfile(dag_catalog_filepath): + existing_files = os.listdir(self.settings.DAG_CATALOG_DIRECTORY) + existing_files_str = "\n".join(existing_files) + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail=f"The process ID '{ogcapppkg.process_description.id}' does not have a matching DAG file named '{dag_filename}' in the DAG catalog.\nThe DAG catalog includes the following files:\n{existing_files_str}", + ) + + if os.path.isfile( + os.path.join(self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename) + ): + # Log warning that file already exists in the deployed dags directory + pass + + shutil.copy2( + dag_catalog_filepath, + self.settings.DEPLOYED_DAGS_DIRECTORY, + ) + + if not os.path.isfile( + os.path.join(self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename) + ): + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail="Failed to copy DAG file to deployed directory", + ) - # For now, we'll just create a dummy process - new_process = ogcapppkg.process_description + ems_api_auth = HTTPBasicAuth( + self.settings.EMS_API_AUTH_USERNAME, + self.settings.EMS_API_AUTH_PASSWORD.get_secret_value(), + ) + timeout = 20 + start_time = time.time() + while time.time() - start_time < timeout: + response = requests.get( + f"{self.settings.EMS_API_URL}/dags/{ogcapppkg.process_description.id}", + auth=ems_api_auth, + ) + data = response.json() + if response.status_code == 404: + pass + elif data["is_paused"]: + self.pause_dag( + self.settings.EMS_API_URL, + ogcapppkg.process_description.id, + ems_api_auth, + pause=False, + ) + elif data["is_active"]: + break + time.sleep(0.5) + else: + raise HTTPException( + status_code=status.HTTP_504_GATEWAY_TIMEOUT, + detail=f"Timeout waiting for DAG '{ogcapppkg.process_description.id}' to be available in Airflow.", + ) - # In a real implementation, you'd save this process to a database + crud.update_process( + self.db, + ogcapppkg.process_description.id, + {"deployment_status": "deployed"}, + ) return Response( status_code=status.HTTP_201_CREATED, - content=f"Process {new_process.id} deployed successfully", + content=f"Process {ogcapppkg.process_description.id} deployed successfully", ) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: - # Simulate replacement process try: - # Here you would typically: - # 1. Check if the process exists - # 2. Validate the new ogcapppkg - # 3. Update the existing process with new data + with self.redis_locking_client.lock(f"replace_process_{processId}"): + # Validate the new ogcapppkg + if ogcapppkg.process_description.id != processId: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Process ID in the path does not match the ID in the request body", + ) - # For now, we'll just print a message - print(f"Process {processId} replaced with new package: {ogcapppkg.id}") + # Update the existing process with new data + crud.update_process( + self.db, processId, ogcapppkg.process_description.model_dump() + ) + + # Update the DAG file + dag_filename = f"{processId}.py" + dag_catalog_filepath = os.path.join( + self.settings.DAG_CATALOG_DIRECTORY, dag_filename + ) + deployed_dag_path = os.path.join( + self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename + ) + + if os.path.exists(dag_catalog_filepath): + shutil.copy2(dag_catalog_filepath, deployed_dag_path) + else: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"DAG file for process {processId} not found in the catalog", + ) + + # Optionally, you might want to refresh the DAG in Airflow + ems_api_auth = HTTPBasicAuth( + self.settings.EMS_API_AUTH_USERNAME, + self.settings.EMS_API_AUTH_PASSWORD.get_secret_value(), + ) + response = requests.post( + f"{self.settings.EMS_API_URL}/dags/{processId}/dagRuns", + auth=ems_api_auth, + json={"is_paused": False}, # Unpause the DAG if it was paused + ) + response.raise_for_status() + + return Response( + status_code=status.HTTP_204_NO_CONTENT, + content=f"Process {processId} replaced successfully", + ) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) def undeploy(self, processId: str) -> None: - # Simulate undeployment process + check_process_integrity(self.db, processId, new_process=False) try: - # Here you would typically: - # 1. Check if the process exists - # 2. Remove the process from the system - # 3. Clean up any associated resources + with self.redis_locking_client.lock(f"undeploy_process_{processId}"): + # Remove the DAG file from the deployed directory + dag_filename = f"{processId}.py" + deployed_dag_path = os.path.join( + self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename + ) + if os.path.exists(deployed_dag_path): + os.remove(deployed_dag_path) + + # Delete the process from the database + crud.delete_process(self.db, processId) + + # Optionally, you might want to pause or delete the DAG in Airflow + ems_api_auth = HTTPBasicAuth( + self.settings.EMS_API_AUTH_USERNAME, + self.settings.EMS_API_AUTH_PASSWORD.get_secret_value(), + ) + response = requests.delete( + f"{self.settings.EMS_API_URL}/dags/{processId}", + auth=ems_api_auth, + ) + response.raise_for_status() - # For now, we'll just print a message - print(f"Process {processId} undeployed successfully") + return Response( + status_code=status.HTTP_204_NO_CONTENT, + content=f"Process {processId} undeployed successfully", + ) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) diff --git a/app-cp/src/openapi_server/utils/__init__.py b/app-cp/src/openapi_server/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app-cp/src/openapi_server/utils/redis.py b/app-cp/src/openapi_server/utils/redis.py new file mode 100644 index 0000000..49851b3 --- /dev/null +++ b/app-cp/src/openapi_server/utils/redis.py @@ -0,0 +1,25 @@ +from contextlib import contextmanager + +import redis +from redis.exceptions import LockError + + +class RedisLock: + def __init__(self, client=None, host="localhost", port=6379, db=0): + if client is None: + client = redis.Redis(host=host, port=port, db=db) + self.client = client + + @contextmanager + def lock(self, lock_id, timeout=10): + """Attempt to acquire a lock within the given timeout period.""" + lock = self.client.lock(lock_id, timeout=timeout) + acquired = lock.acquire(blocking=True, blocking_timeout=timeout) + try: + if acquired: + yield lock + else: + raise LockError(f"Could not acquire lock for ID {lock_id}") + finally: + if acquired: + lock.release() diff --git a/app-cp/src/unity_sps_ogc_processes_api/__init__.py b/app-cp/src/unity_sps_ogc_processes_api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py index e2e7df9..f97f0c4 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py @@ -3,10 +3,18 @@ import importlib import pkgutil -from fastapi import APIRouter, Body, Path, Query +from fastapi import APIRouter, Body, Depends, Path, Query +from sqlalchemy.orm import Session import openapi_server.impl +from openapi_server.config.config import Settings +from openapi_server.utils.redis import RedisLock from unity_sps_ogc_processes_api.apis.dru_api_base import BaseDRUApi +from unity_sps_ogc_processes_api.dependencies import ( + get_db, + get_redis_locking_client, + get_settings, +) from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg @@ -33,6 +41,9 @@ response_model_by_alias=True, ) async def deploy( + settings: Settings = Depends(get_settings), + redis_locking_client: RedisLock = Depends(get_redis_locking_client), + db: Session = Depends(get_db), ogcapppkg: Ogcapppkg = Body( None, description="An OGC Application Package used to deploy a new process." ), @@ -43,7 +54,8 @@ async def deploy( ), ) -> None: """Deploys a process. For more information, see [Section 6.3](http://docs.ogc.org/DRAFTS/20-044.html#_87a6983e-d060-458c-95ab-27e232e64822).""" - return BaseDRUApi.subclasses[0]().deploy(ogcapppkg, w) + dru_api = BaseDRUApi.subclasses[0](settings, redis_locking_client, db) + return dru_api.deploy(ogcapppkg, w) @router.put( @@ -66,13 +78,17 @@ async def deploy( response_model_by_alias=True, ) async def replace( + settings: Settings = Depends(get_settings), + redis_locking_client: RedisLock = Depends(get_redis_locking_client), + db: Session = Depends(get_db), processId: str = Path(..., description=""), ogcapppkg: Ogcapppkg = Body( None, description="An OGC Application Package used to deploy a new process." ), ) -> None: """Replaces a process. For more information, see [Section 6.4](http://docs.ogc.org/DRAFTS/20-044.html#_18582f42-ebc6-4284-9333-c089068f62b6).""" - return BaseDRUApi.subclasses[0]().replace(processId, ogcapppkg) + dru_api = BaseDRUApi.subclasses[0](settings, redis_locking_client, db) + return dru_api.replace(processId, ogcapppkg) @router.delete( @@ -91,7 +107,11 @@ async def replace( response_model_by_alias=True, ) async def undeploy( + settings: Settings = Depends(get_settings), + redis_locking_client: RedisLock = Depends(get_redis_locking_client), + db: Session = Depends(get_db), processId: str = Path(..., description=""), ) -> None: """Undeploys a process. For more information, see [Section 6.5](http://docs.ogc.org/DRAFTS/20-044.html#_16391f9e-538f-4a84-9710-72a6bab82842).""" - return BaseDRUApi.subclasses[0]().undeploy(processId) + dru_api = BaseDRUApi.subclasses[0](settings, redis_locking_client, db) + return dru_api.undeploy(processId) diff --git a/app-cp/src/unity_sps_ogc_processes_api/dependencies.py b/app-cp/src/unity_sps_ogc_processes_api/dependencies.py new file mode 100644 index 0000000..027171b --- /dev/null +++ b/app-cp/src/unity_sps_ogc_processes_api/dependencies.py @@ -0,0 +1,24 @@ +from functools import lru_cache + +from openapi_server.config.config import Settings +from openapi_server.database import SessionLocal +from openapi_server.utils.redis import RedisLock + + +@lru_cache +def get_settings(): + return Settings() + + +@lru_cache() +def get_redis_locking_client(): + settings = get_settings() + return RedisLock(host=settings.REDIS_HOST, port=settings.REDIS_PORT) + + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() diff --git a/app-cp/src/unity_sps_ogc_processes_api/main.py b/app-cp/src/unity_sps_ogc_processes_api/main.py index 2693fd6..76a6f8e 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/main.py +++ b/app-cp/src/unity_sps_ogc_processes_api/main.py @@ -13,8 +13,9 @@ """ # noqa: E501 -from fastapi import FastAPI +from fastapi import Depends, FastAPI +from openapi_server.database import engine, models from unity_sps_ogc_processes_api.apis.api_api import router as APIApiRouter from unity_sps_ogc_processes_api.apis.conformance_api import ( router as ConformanceApiRouter, @@ -25,6 +26,13 @@ router as LandingPageApiRouter, ) from unity_sps_ogc_processes_api.apis.processes_api import router as ProcessesApiRouter +from unity_sps_ogc_processes_api.dependencies import ( + get_db, + get_redis_locking_client, + get_settings, +) + +models.Base.metadata.create_all(bind=engine) # Create database tables app = FastAPI( title="OGC API - Processes", @@ -34,7 +42,14 @@ app.include_router(APIApiRouter) app.include_router(ConformanceApiRouter) -app.include_router(DRUApiRouter) +app.include_router( + DRUApiRouter, + dependencies=[ + Depends(get_settings), + Depends(get_redis_locking_client), + Depends(get_db), + ], +) app.include_router(JobsApiRouter) app.include_router(LandingPageApiRouter) app.include_router(ProcessesApiRouter) diff --git a/app-cp/tests/conftest.py b/app-cp/tests/conftest.py index a881b4f..6b98e63 100644 --- a/app-cp/tests/conftest.py +++ b/app-cp/tests/conftest.py @@ -1,17 +1,508 @@ +import json +import os +import re + +import fakeredis import pytest -from fastapi import FastAPI +from fastapi import status + +# from fastapi.encoders import jsonable_encoder from fastapi.testclient import TestClient +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.pool import StaticPool + +from openapi_server.database import Base +from openapi_server.database.models import Process +from openapi_server.utils.redis import RedisLock +from unity_sps_ogc_processes_api.dependencies import ( + get_db, + get_redis_locking_client, + get_settings, +) +from unity_sps_ogc_processes_api.main import app + +settings = get_settings() +SQLALCHEMY_DATABASE_URL = settings.DB_URL +engine = create_engine( + SQLALCHEMY_DATABASE_URL, + connect_args={"check_same_thread": False}, + poolclass=StaticPool, +) +TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) +Base.metadata.create_all(bind=engine) + + +def override_get_db(): + try: + db = TestingSessionLocal() + yield db + finally: + db.close() + + +def override_get_redis_locking_client(): + redis_client = fakeredis.FakeRedis(decode_responses=True, version=(6,)) + return RedisLock(redis_client) + + +app.dependency_overrides[get_db] = override_get_db +app.dependency_overrides[get_redis_locking_client] = override_get_redis_locking_client -from unity_sps_ogc_processes_api.main import app as application +@pytest.fixture(scope="session") +def test_directory(): + """Returns the directory path of the current test session.""" + return os.path.dirname(os.path.abspath(__file__)) -@pytest.fixture -def app() -> FastAPI: - application.dependency_overrides = {} - return application +@pytest.fixture(scope="session", autouse=True) +def fake_filesystem(fs_session, test_directory): # pylint:disable=invalid-name + """Variable name 'fs' causes a pylint warning. Provide a longer name + acceptable to pylint for use in tests. + """ + fs_session.add_real_directory( + os.path.join(test_directory, "..", "..", "process_descriptions") + ) + fs_session.add_real_directory(os.path.join(test_directory), "test_data") + fs_session.create_dir(settings.DAG_CATALOG_DIRECTORY) + fs_session.create_file( + os.path.join(settings.DAG_CATALOG_DIRECTORY, "cwltool_help_dag.py"), + contents="test", + ) + fs_session.create_file( + os.path.join(settings.DAG_CATALOG_DIRECTORY, "EchoProcess.py"), contents="test" + ) + fs_session.create_dir(settings.DEPLOYED_DAGS_DIRECTORY) + yield fs_session -@pytest.fixture -def client(app) -> TestClient: +@pytest.fixture(scope="session") +def client(): return TestClient(app) + + +@pytest.fixture(scope="function", autouse=True) +def mock_get_existing_dag(requests_mock): + return requests_mock.get( + re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)$"), + [ + { + "json": { + "dag_id": "string", + "dag_display_name": "string", + "root_dag_id": "string", + "is_paused": False, + "is_active": True, + "is_subdag": True, + "last_parsed_time": "2019-08-24T14:15:22.548326+00:00", + "last_pickled": "2019-08-24T14:15:22.548326+00:00", + "last_expired": "2019-08-24T14:15:22.548326+00:00", + "scheduler_lock": True, + "pickle_id": "string", + "default_view": "string", + "fileloc": "string", + "file_token": "string", + "owners": ["string"], + "description": "string", + "schedule_interval": { + "__type": "string", + "days": 0, + "seconds": 0, + "microseconds": 0, + }, + "timetable_description": "string", + "tags": [{"name": "string"}], + "max_active_tasks": 0, + "max_active_runs": 0, + "has_task_concurrency_limits": True, + "has_import_errors": True, + "next_dagrun": "2019-08-24T14:15:22.548326+00:00", + "next_dagrun_data_interval_start": "2019-08-24T14:15:22.548326+00:00", + "next_dagrun_data_interval_end": "2019-08-24T14:15:22.548326+00:00", + "next_dagrun_create_after": "2019-08-24T14:15:22.548326+00:00", + } + }, + { + "json": { + "dag_id": "string", + "dag_display_name": "string", + "root_dag_id": "string", + "is_paused": True, + "is_active": False, + "is_subdag": True, + "last_parsed_time": "2019-08-24T14:15:22.548326+00:00", + "last_pickled": "2019-08-24T14:15:22.548326+00:00", + "last_expired": "2019-08-24T14:15:22.548326+00:00", + "scheduler_lock": True, + "pickle_id": "string", + "default_view": "string", + "fileloc": "string", + "file_token": "string", + "owners": ["string"], + "description": "string", + "schedule_interval": { + "__type": "string", + "days": 0, + "seconds": 0, + "microseconds": 0, + }, + "timetable_description": "string", + "tags": [{"name": "string"}], + "max_active_tasks": 0, + "max_active_runs": 0, + "has_task_concurrency_limits": True, + "has_import_errors": True, + "next_dagrun": "2019-08-24T14:15:22.548326+00:00", + "next_dagrun_data_interval_start": "2019-08-24T14:15:22.548326+00:00", + "next_dagrun_data_interval_end": "2019-08-24T14:15:22.548326+00:00", + "next_dagrun_create_after": "2019-08-24T14:15:22.548326+00:00", + } + }, + ], + ) + + +@pytest.fixture(scope="function", autouse=True) +def mock_patch_existing_dag(requests_mock): + return requests_mock.patch( + re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)$"), + [ + { + "json": { + "dag_id": "string", + "dag_display_name": "string", + "root_dag_id": "string", + "is_paused": False, + "is_active": True, + "is_subdag": True, + "last_parsed_time": "2019-08-24T14:15:22Z", + "last_pickled": "2019-08-24T14:15:22Z", + "last_expired": "2019-08-24T14:15:22Z", + "scheduler_lock": True, + "pickle_id": "string", + "default_view": "string", + "fileloc": "string", + "file_token": "string", + "owners": ["string"], + "description": "string", + "schedule_interval": { + "__type": "string", + "days": 0, + "seconds": 0, + "microseconds": 0, + }, + "timetable_description": "string", + "tags": [{"name": "string"}], + "max_active_tasks": 0, + "max_active_runs": 0, + "has_task_concurrency_limits": True, + "has_import_errors": True, + "next_dagrun": "2019-08-24T14:15:22Z", + "next_dagrun_data_interval_start": "2019-08-24T14:15:22Z", + "next_dagrun_data_interval_end": "2019-08-24T14:15:22Z", + "next_dagrun_create_after": "2019-08-24T14:15:22Z", + } + }, + { + "json": { + "dag_id": "string", + "dag_display_name": "string", + "root_dag_id": "string", + "is_paused": True, + "is_active": False, + "is_subdag": True, + "last_parsed_time": "2019-08-24T14:15:22Z", + "last_pickled": "2019-08-24T14:15:22Z", + "last_expired": "2019-08-24T14:15:22Z", + "scheduler_lock": True, + "pickle_id": "string", + "default_view": "string", + "fileloc": "string", + "file_token": "string", + "owners": ["string"], + "description": "string", + "schedule_interval": { + "__type": "string", + "days": 0, + "seconds": 0, + "microseconds": 0, + }, + "timetable_description": "string", + "tags": [{"name": "string"}], + "max_active_tasks": 0, + "max_active_runs": 0, + "has_task_concurrency_limits": True, + "has_import_errors": True, + "next_dagrun": "2019-08-24T14:15:22Z", + "next_dagrun_data_interval_start": "2019-08-24T14:15:22Z", + "next_dagrun_data_interval_end": "2019-08-24T14:15:22Z", + "next_dagrun_create_after": "2019-08-24T14:15:22Z", + } + }, + ], + ) + + +@pytest.fixture(scope="function", autouse=True) +def mock_delete_existing_dag(requests_mock): + return requests_mock.delete( + re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)$"), + status_code=status.HTTP_204_NO_CONTENT, + ) + + +@pytest.fixture(scope="function", autouse=True) +def mock_post_existing_dag_new_dagrun(requests_mock): + return requests_mock.post( + re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns$"), + json={ + "dag_run_id": "string", + "logical_date": "2019-08-24T14:15:22Z", + "execution_date": "2019-08-24T14:15:22Z", + "data_interval_start": "2019-08-24T14:15:22Z", + "data_interval_end": "2019-08-24T14:15:22Z", + "conf": {}, + "note": "string", + }, + ) + + +@pytest.fixture(scope="function", autouse=True) +def mock_get_existing_dag_dagruns(requests_mock): + return requests_mock.get( + re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns$"), + json={ + "dag_runs": [ + { + "dag_run_id": "string", + "dag_id": "string", + "logical_date": "2019-08-24T14:15:22.548326+00:00", + "execution_date": "2019-08-24T14:15:22.548326+00:00", + "start_date": "2019-08-24T14:15:22.548326+00:00", + "end_date": "2019-08-24T14:15:22.548326+00:00", + "data_interval_start": "2019-08-24T14:15:22.548326+00:00", + "data_interval_end": "2019-08-24T14:15:22.548326+00:00", + "last_scheduling_decision": "2019-08-24T14:15:22.548326+00:00", + "run_type": "backfill", + "state": "queued", + "external_trigger": True, + "conf": {}, + "note": "string", + } + ], + "total_entries": 1, + }, + ) + + +@pytest.fixture(scope="function", autouse=True) +def mock_get_existing_running_dag_dagruns(requests_mock): + return requests_mock.get( + re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns\\?state=running$"), + json={ + "dag_runs": [ + { + "dag_run_id": "string", + "dag_id": "string", + "logical_date": "2019-08-24T14:15:22.548326+00:00", + "execution_date": "2019-08-24T14:15:22.548326+00:00", + "start_date": "2019-08-24T14:15:22.548326+00:00", + "end_date": "2019-08-24T14:15:22.548326+00:00", + "data_interval_start": "2019-08-24T14:15:22.548326+00:00", + "data_interval_end": "2019-08-24T14:15:22.548326+00:00", + "last_scheduling_decision": "2019-08-24T14:15:22.548326+00:00", + "run_type": "backfill", + "state": "running", + "external_trigger": True, + "conf": {}, + "note": "string", + } + ], + "total_entries": 1, + }, + ) + + +@pytest.fixture(scope="function", autouse=True) +def mock_patch_existing_running_dag_dagrun(requests_mock): + return requests_mock.patch( + re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)$"), + json={ + "dag_run_id": "string", + "dag_id": "string", + "logical_date": "2019-08-24T14:15:22.548326+00:00", + "execution_date": "2019-08-24T14:15:22.548326+00:00", + "start_date": "2019-08-24T14:15:22.548326+00:00", + "end_date": "2019-08-24T14:15:22.548326+00:00", + "data_interval_start": "2019-08-24T14:15:22.548326+00:00", + "data_interval_end": "2019-08-24T14:15:22.548326+00:00", + "last_scheduling_decision": "2019-08-24T14:15:22.548326+00:00", + "run_type": "backfill", + "state": "queued", + "external_trigger": True, + "conf": {}, + "note": "string", + }, + ) + + +@pytest.fixture(scope="function", autouse=True) +def mock_get_existing_dag_dagrun(requests_mock): + return requests_mock.get( + re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)$"), + json={ + "dag_run_id": "string", + "dag_id": "string", + "logical_date": "2019-08-24T14:15:22.548326+00:00", + "execution_date": "2019-08-24T14:15:22.548326+00:00", + "start_date": "2019-08-24T14:15:22.548326+00:00", + "end_date": "2019-08-24T14:15:22.548326+00:00", + "data_interval_start": "2019-08-24T14:15:22.548326+00:00", + "data_interval_end": "2019-08-24T14:15:22.548326+00:00", + "last_scheduling_decision": "2019-08-24T14:15:22.548326+00:00", + "run_type": "backfill", + "state": "queued", + "external_trigger": True, + "conf": {}, + "note": "string", + }, + ) + + +@pytest.fixture(scope="function", autouse=True) +def mock_delete_existing_dag_dagrun(requests_mock): + return requests_mock.delete( + re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)$"), + status_code=status.HTTP_204_NO_CONTENT, + ) + + +@pytest.fixture(scope="function", autouse=True) +def mock_get_existing_running_dag_dagrun_tasks(requests_mock): + return requests_mock.get( + re.compile( + f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)/taskInstances$" + ), + json={ + "task_instances": [ + { + "task_id": "string", + "task_display_name": "string", + "dag_id": "string", + "dag_run_id": "string", + "execution_date": "string", + "start_date": "string", + "end_date": "string", + "duration": 0, + "state": None, + "try_number": 0, + "map_index": 0, + "max_tries": 0, + "hostname": "string", + "unixname": "string", + "pool": "string", + "pool_slots": 0, + "queue": "string", + "priority_weight": 0, + "operator": "string", + "queued_when": "string", + "pid": 0, + "executor_config": "string", + "sla_miss": { + "task_id": "string", + "dag_id": "string", + "execution_date": "string", + "email_sent": True, + "timestamp": "string", + "description": "string", + "notification_sent": True, + }, + "rendered_map_index": "string", + "rendered_fields": {}, + "trigger": { + "id": 0, + "classpath": "string", + "kwargs": "string", + "created_date": "string", + "triggerer_id": 0, + }, + "triggerer_job": { + "id": 0, + "dag_id": "string", + "state": "string", + "job_type": "string", + "start_date": "string", + "end_date": "string", + "latest_heartbeat": "string", + "executor_class": "string", + "hostname": "string", + "unixname": "string", + }, + "note": "string", + } + ], + "total_entries": 0, + }, + ) + + +@pytest.fixture(scope="function", autouse=True) +def mock_patch_existing_running_dag_dagrun_task(requests_mock): + return requests_mock.patch( + re.compile( + f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)/taskInstances/([^/]*)$" + ), + json={ + "task_id": "string", + "dag_id": "string", + "execution_date": "string", + "dag_run_id": "string", + }, + ) + + +@pytest.fixture(scope="function") +def deploy_process(test_directory, client): + data_filename = os.path.join( + test_directory, "..", "process_descriptions", "cwltool_help_dag.json" + ) + f = open(data_filename) + process_json = json.load(f) + process = Process.model_validate(process_json) + response = client.post("/processes", json=process.model_dump()) + assert response.status_code == status.HTTP_200_OK + data = response.json() + process = Process.model_validate(data) + assert process.id == "cwltool_help_dag" + + yield process + + response = client.delete(f"/processes/{process.id}", params={"force": True}) + assert response.status_code == status.HTTP_204_NO_CONTENT + + +# @pytest.fixture(scope="function") +# def execute_process( +# test_directory, mock_post_existing_dag_new_dagrun, client, deploy_process +# ): +# data_filename = os.path.join( +# test_directory, "test_data/execution_requests/execute_cwltool_help_dag.json" +# ) +# f = open(data_filename) +# execute_json = json.load(f) +# execute = Execute.model_validate(execute_json) +# response = client.post( +# f"/processes/{deploy_process.id}/execution", json=jsonable_encoder(execute) +# ) +# assert response.status_code == status.HTTP_200_OK +# data = response.json() +# status_info = StatusInfo.model_validate(data) + +# yield status_info + +# response = client.delete(f"/jobs/{status_info.jobID}") +# assert response.status_code == status.HTTP_200_OK +# data = response.json() +# status_info = StatusInfo.model_validate(data) +# assert status_info.status == StatusCode.dismissed.value diff --git a/app-cp/tests/test_dru_api.py b/app-cp/tests/test_dru_api.py index 7e53f9b..a05433e 100644 --- a/app-cp/tests/test_dru_api.py +++ b/app-cp/tests/test_dru_api.py @@ -1,107 +1,27 @@ # coding: utf-8 +import json +import os + import pytest from fastapi.testclient import TestClient -from unity_sps_ogc_processes_api.models.execution_unit import ExecutionUnit -from unity_sps_ogc_processes_api.models.input_description import InputDescription -from unity_sps_ogc_processes_api.models.job_control_options import JobControlOptions -from unity_sps_ogc_processes_api.models.link import Link -from unity_sps_ogc_processes_api.models.metadata import Metadata -from unity_sps_ogc_processes_api.models.metadata_one_of import MetadataOneOf -from unity_sps_ogc_processes_api.models.model_schema import ModelSchema -from unity_sps_ogc_processes_api.models.ogcapppkg import ( - Ogcapppkg, - OgcapppkgExecutionUnit, -) -from unity_sps_ogc_processes_api.models.output_description import OutputDescription -from unity_sps_ogc_processes_api.models.process import Process -from unity_sps_ogc_processes_api.models.reference import Reference +from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg @pytest.fixture def sample_ogcapppkg(): - return Ogcapppkg( - process_description=Process( - id="example-process-id", - version="1.0.0", - title="Example Process Title", - description="This process performs an example task.", - keywords=["example", "process", "OGC"], - metadata=[ - Metadata( - MetadataOneOf( - href="http://example.com/metadata", - rel="related", - type="application/json", - hreflang="en", - title="Example Metadata", - ) - ) - ], - job_control_options=[ - JobControlOptions.SYNC_MINUS_EXECUTE, - JobControlOptions.ASYNC_MINUS_EXECUTE, - ], - links=[ - Link( - href="http://example.com/process", - rel="self", - type="application/json", - hreflang="en", - title="Process Description", - ) - ], - inputs={ - "input1": InputDescription( - title="Input 1 Title", - description="Description of Input 1", - min_occurs=1, - max_occurs=1, - schema=ModelSchema( - Reference(ref="#/components/schemas/ExampleSchema") - ), - formats=[ - { - "mediaType": "application/json", - "encoding": "UTF-8", - "schema": "http://json-schema.org/draft-07/schema#", - } - ], - ) - }, - outputs={ - "output1": OutputDescription( - title="Output 1 Title", - description="Description of Output 1", - schema=ModelSchema( - actual_instance=Reference( - ref="#/components/schemas/ExampleSchema" - ) - ), - formats=[ - { - "mediaType": "application/json", - "encoding": "UTF-8", - "schema": "http://json-schema.org/draft-07/schema#", - } - ], - ) - }, - ), - execution_unit=OgcapppkgExecutionUnit( - ExecutionUnit( - type="docker", - image="example/image:latest", - deployment="cloud", - config={ - "cpu": "2", - "memory": "4GiB", - "env": {"EXAMPLE_ENV_VAR": "value"}, - }, - ) - ), - ) + # Get the current directory of the test file + current_dir = os.path.dirname(os.path.abspath(__file__)) + + # Construct the path to echoProcess.json + json_file_path = os.path.join(current_dir, "echoProcess.json") + + # Read the JSON file + with open(json_file_path, "r") as file: + data = json.load(file) + + return Ogcapppkg(**data) def test_deploy(client: TestClient, sample_ogcapppkg): @@ -113,41 +33,42 @@ def test_deploy(client: TestClient, sample_ogcapppkg): assert response.status_code == 201 -# def test_replace(client: TestClient, sample_ogcapppkg): -# """Test case for replace""" -# process_id = "test_process" -# response = client.put( -# f"/processes/{process_id}", -# json=sample_ogcapppkg.dict(), -# ) - -# assert response.status_code == 204 - - -# def test_undeploy(client: TestClient): -# """Test case for undeploy""" -# process_id = "test_process" -# response = client.delete(f"/processes/{process_id}") - -# assert response.status_code == 204 - +def test_replace(client: TestClient, sample_ogcapppkg): + """Test case for replace""" + process_id = "EchoProcess" + response = client.put( + f"/processes/{process_id}", + json=sample_ogcapppkg.model_dump(), + ) + assert response.status_code == 204 -# def test_deploy_conflict(client: TestClient, sample_ogcapppkg): -# """Test case for deploy when process already exists""" -# # First, deploy the process -# client.post("/processes", json=sample_ogcapppkg.dict()) -# # Try to deploy the same process again -# response = client.post("/processes", json=sample_ogcapppkg.dict()) +def test_undeploy(client: TestClient): + """Test case for undeploy""" + process_id = "EchoProcess" + response = client.delete(f"/processes/{process_id}") + assert response.status_code == 204 -# assert response.status_code == 409 -# assert "already exists" in response.json()["detail"] +def test_deploy_conflict(client: TestClient, sample_ogcapppkg): + """Test case for deploy when process already exists""" + # First, deploy the process + response = client.post( + "/processes", + json=sample_ogcapppkg.model_dump(exclude_none=True, by_alias=True), + ) + # Try to deploy the same process again + response = client.post( + "/processes", + json=sample_ogcapppkg.model_dump(exclude_none=True, by_alias=True), + ) + assert response.status_code == 409 + assert "already exists" in response.json()["detail"] -# def test_undeploy_not_found(client: TestClient): -# """Test case for undeploy when process doesn't exist""" -# process_id = "non_existent_process" -# response = client.delete(f"/processes/{process_id}") -# assert response.status_code == 404 -# assert "not found" in response.json()["detail"] +def test_undeploy_not_found(client: TestClient): + """Test case for undeploy when process doesn't exist""" + process_id = "non_existent_process" + response = client.delete(f"/processes/{process_id}") + assert response.status_code == 404 + assert "not found" in response.json()["detail"] From fcb04ac2f5abdda77d841ac44032a6c8bdd48599 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 29 Aug 2024 13:07:09 -0700 Subject: [PATCH 19/60] feat: Initial autogenerated FastAPI implementation --- Dockerfile | 7 ++-- app-cp/pyproject.toml | 35 +++++++++++++++++++ .../src/unity_sps_ogc_processes_api/main.py | 14 +++++--- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index e9a8e43..0e2054f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,14 +5,15 @@ FROM python:3.12-slim RUN apt-get update && apt-get install -y git gcc libpq-dev # Copy the current directory contents into the container at /app -COPY ./app /app +COPY ./app-cp /app + +WORKDIR /app # Install any needed packages specified in requirements.txt -COPY pyproject.toml . RUN pip install . # Make port 80 available to the world outside this container EXPOSE 80 # Run app.py when the container launches -CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] +CMD ["uvicorn", "unity_sps_ogc_processes_api.main:app", "--host", "0.0.0.0", "--port", "80"] diff --git a/app-cp/pyproject.toml b/app-cp/pyproject.toml index c2826df..e57b9df 100644 --- a/app-cp/pyproject.toml +++ b/app-cp/pyproject.toml @@ -2,6 +2,41 @@ requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta" +[project] +name = "unity-sps-ogc-processes-api" +version = "1.0.0" +authors = [ + { name = "Drew Meyers", email = "drew.meyers@jpl.nasa.gov" }, +] +description = "The Unity science processing service area's OGC Processes API." +classifiers = [ + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Operating System :: OS Independent", + "Intended Audience :: Science/Research", +] + +dependencies = [ + "requests==2.31.0", + "fastapi==0.110.0", + "pydantic==2.6.4", + "SQLAlchemy==2.0.28", + "pydantic-settings==2.0.0", + "uvicorn==0.27.1", + "psycopg2-binary==2.9.9", + "redis==5.0.4", + "apache-airflow-client @ git+https://github.com/apache/airflow-client-python.git@2.9.0" +] + + +[tool.setuptools.packages.find] +where = ["src"] +include = ["unity_sps_ogc_processes_api*", "openapi_server*"] + + [tool.black] line-length = 88 exclude = ''' diff --git a/app-cp/src/unity_sps_ogc_processes_api/main.py b/app-cp/src/unity_sps_ogc_processes_api/main.py index 76a6f8e..1dce037 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/main.py +++ b/app-cp/src/unity_sps_ogc_processes_api/main.py @@ -32,12 +32,18 @@ get_settings, ) -models.Base.metadata.create_all(bind=engine) # Create database tables +# Create database tables +models.Base.metadata.create_all(bind=engine) app = FastAPI( - title="OGC API - Processes", - description="Example API Definition for OGC API - Processes", - version="0.1", + version="2.0.0", + title="Unity Processing API conforming to the Draft of OGC API - Processes - Part 2: Deploy, Replace, Undeploy", + description="This document is an API definition document provided alongside the OGC API - Processes standard. The OGC API - Processes Standard specifies a processing interface to communicate over a RESTful protocol using JavaScript Object Notation (JSON) encodings. The specification allows for the wrapping of computational tasks into executable processes that can be offered by a server and be invoked by a client application.", + license={ + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html", + }, + servers=[], ) app.include_router(APIApiRouter) From 9e9e8c04b8dcdb673243f816e1c7968c23f6affd Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 29 Aug 2024 13:09:37 -0700 Subject: [PATCH 20/60] feat: Initial autogenerated FastAPI implementation --- app-cp/requirements.txt | 36 ------------------------------------ app-cp/setup.cfg | 20 -------------------- 2 files changed, 56 deletions(-) delete mode 100644 app-cp/requirements.txt delete mode 100644 app-cp/setup.cfg diff --git a/app-cp/requirements.txt b/app-cp/requirements.txt deleted file mode 100644 index b3ea81f..0000000 --- a/app-cp/requirements.txt +++ /dev/null @@ -1,36 +0,0 @@ -aiofiles==23.1.0 -aniso8601==7.0.0 -async-exit-stack==1.0.1 -async-generator==1.10 -certifi==2023.7.22 -chardet==4.0.0 -click==7.1.2 -dnspython==2.6.1 -email-validator==2.0.0 -fastapi==0.109.2 -graphene==2.1.8 -graphql-core==2.3.2 -graphql-relay==2.0.1 -h11==0.12.0 -httptools==0.1.2 -httpx==0.24.1 -idna==3.7 -itsdangerous==1.1.0 -Jinja2==3.1.4 -MarkupSafe==2.0.1 -orjson==3.9.15 -promise==2.3 -pydantic>=2 -python-dotenv==0.17.1 -python-multipart==0.0.7 -PyYAML==5.4.1 -requests==2.32.0 -Rx==1.6.1 -starlette==0.36.3 -typing-extensions==4.8.0 -ujson==4.0.2 -urllib3==1.26.19 -uvicorn==0.13.4 -uvloop==0.19.0 -watchgod==0.7 -websockets==10.0 diff --git a/app-cp/setup.cfg b/app-cp/setup.cfg deleted file mode 100644 index 1747ec5..0000000 --- a/app-cp/setup.cfg +++ /dev/null @@ -1,20 +0,0 @@ -[metadata] -name = unity_sps_ogc_processes_api -version = 0.1 -description = Example API Definition for OGC API - Processes -long_description = file: README.md -keywords = OpenAPI OGC API - Processes -python_requires = >= 3.7.* -classifiers = - Operating System :: OS Independent - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.7 - -[options] -install_requires = fastapi[all] -setup_requires = setuptools -package_dir = =src -packages = find_namespace: - -[options.packages.find] -where = src From 0dd8b2bff60c30106f6e6fb11edd64a8730748a9 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 29 Aug 2024 15:51:52 -0700 Subject: [PATCH 21/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/jobs_api.py | 171 +++++++++++++----- .../src/openapi_server/impl/processes_api.py | 134 ++++++++------ .../apis/jobs_api.py | 37 +++- .../apis/processes_api.py | 33 +++- 4 files changed, 270 insertions(+), 105 deletions(-) diff --git a/app-cp/src/openapi_server/impl/jobs_api.py b/app-cp/src/openapi_server/impl/jobs_api.py index 9f07475..e59a2b0 100644 --- a/app-cp/src/openapi_server/impl/jobs_api.py +++ b/app-cp/src/openapi_server/impl/jobs_api.py @@ -1,16 +1,74 @@ +import uuid from datetime import datetime from typing import Dict +import requests +from fastapi import HTTPException +from fastapi import status as fastapi_status +from requests.auth import HTTPBasicAuth +from sqlalchemy.orm import Session + +from openapi_server.config.config import Settings +from openapi_server.database import crud +from openapi_server.utils.redis import RedisLock from unity_sps_ogc_processes_api.apis.jobs_api_base import BaseJobsApi +from unity_sps_ogc_processes_api.models.execute import Execute from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData from unity_sps_ogc_processes_api.models.job_list import JobList -from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.status_code import StatusCode from unity_sps_ogc_processes_api.models.status_info import StatusInfo class JobsApiImpl(BaseJobsApi): + def __init__( + self, settings: Settings, redis_locking_client: RedisLock, db: Session + ): + self.settings = settings + self.redis_locking_client = redis_locking_client + self.db = db + self.ems_api_auth = HTTPBasicAuth( + settings.EMS_API_AUTH_USERNAME, + settings.EMS_API_AUTH_PASSWORD.get_secret_value(), + ) + + def check_job_integrity(self, job_id: str, new_job: bool): + try: + job = crud.get_job(self.db, job_id) + if new_job and job is not None: + raise ValueError + except crud.NoResultFound: + if not new_job: + raise HTTPException( + status_code=fastapi_status.HTTP_404_NOT_FOUND, + detail=f"Job with ID '{job_id}' not found", + ) + except crud.MultipleResultsFound: + raise HTTPException( + status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Multiple jobs found with same ID '{job_id}', data integrity error", + ) + except ValueError: + raise HTTPException( + status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Existing job with ID '{job_id}' already exists", + ) + return job + def dismiss(self, jobId: str) -> StatusInfo: + job = self.check_job_integrity(jobId, new_job=False) + try: + response = requests.delete( + f"{self.settings.EMS_API_URL}/dags/{job.processID}/dagRuns/{job.jobID}", + auth=self.ems_api_auth, + ) + response.raise_for_status() + except requests.exceptions.HTTPError as e: + raise HTTPException( + status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to delete DAG run {job.jobID} for DAG {job.processID}: {e}", + ) + + crud.delete_job(self.db, job) return StatusInfo( type="process", job_id=jobId, @@ -20,49 +78,80 @@ def dismiss(self, jobId: str) -> StatusInfo: ) def get_jobs(self) -> JobList: + jobs = crud.get_jobs(self.db) + job_status_infos = [StatusInfo.model_validate(job) for job in jobs] return JobList( - jobs=[ - StatusInfo( - type="process", - job_id="job1", - status=StatusCode.RUNNING, - created=datetime.now(), - updated=datetime.now(), - ), - StatusInfo( - type="process", - job_id="job2", - status=StatusCode.SUCCESSFUL, - created=datetime.now(), - started=datetime.now(), - finished=datetime.now(), - updated=datetime.now(), - ), - ], - links=[ - Link( - href="http://example.com/api/jobs", - rel="self", - type="application/json", - title="this document", - ) - ], + jobs=job_status_infos, + links=[], ) def get_result(self, jobId: str, prefer: str) -> Dict[str, InlineOrRefData]: - return { - "output1": InlineOrRefData(href="http://example.com/result1"), - "output2": InlineOrRefData(href="http://example.com/result2"), - } + self.check_job_integrity(jobId, new_job=False) + results = crud.get_results(self.db, jobId) + return {result.name: InlineOrRefData(href=result.href) for result in results} def get_status(self, jobId: str) -> StatusInfo: - return StatusInfo( - type="process", - job_id=jobId, - status=StatusCode.SUCCESSFUL, - created=datetime.now(), - started=datetime.now(), - finished=datetime.now(), - updated=datetime.now(), - progress=100, - ) + job = self.check_job_integrity(jobId, new_job=False) + job = StatusInfo.model_validate(job) + + try: + response = requests.get( + f"{self.settings.EMS_API_URL}/dags/{job.processID}/dagRuns/{job.jobID}", + auth=self.ems_api_auth, + ) + response.raise_for_status() + except requests.exceptions.HTTPError as e: + raise HTTPException( + status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to fetch DAG run {job.jobID} for DAG {job.processID}: {e}", + ) + + execution_status_conversion_dict = { + "queued": StatusCode.ACCEPTED, + "running": StatusCode.RUNNING, + "success": StatusCode.SUCCESSFUL, + "failed": StatusCode.FAILED, + } + data = response.json() + current_execution_status = execution_status_conversion_dict[data["state"]] + if job.status != current_execution_status: + job.status = current_execution_status + job.updated = datetime.now() + + end_date_str = data.get("end_date", None) + if end_date_str: + job.finished = datetime.fromisoformat(end_date_str) + + return crud.update_job(self.db, job) + + def execute(self, processId: str, execute: Execute) -> StatusInfo: + job_id = str(uuid.uuid4()) + logical_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") + data = { + "dag_run_id": job_id, + "logical_date": logical_date, + "conf": execute.model_dump(), + } + + try: + response = requests.post( + f"{self.settings.EMS_API_URL}/dags/{processId}/dagRuns", + json=data, + auth=self.ems_api_auth, + ) + response.raise_for_status() + self.check_job_integrity(job_id, new_job=True) + job = StatusInfo( + jobID=job_id, + processID=processId, + type="process", + status=StatusCode.ACCEPTED, + created=datetime.now(), + updated=datetime.now(), + ) + return crud.create_job(self.db, job) + except requests.exceptions.RequestException as e: + raise HTTPException( + status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to start DAG run {job_id} with DAG {processId}: {e}", + ) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index fb09024..2943b85 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -1,59 +1,54 @@ +import uuid +from datetime import datetime + +import requests +from fastapi import HTTPException +from fastapi import status as fastapi_status +from requests.auth import HTTPBasicAuth +from sqlalchemy.orm import Session + +from openapi_server.config.config import Settings +from openapi_server.database import crud +from openapi_server.utils.redis import RedisLock from unity_sps_ogc_processes_api.apis.processes_api_base import BaseProcessesApi from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows -from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.process import Process from unity_sps_ogc_processes_api.models.process_list import ProcessList from unity_sps_ogc_processes_api.models.process_summary import ProcessSummary +from unity_sps_ogc_processes_api.models.status_code import StatusCode from unity_sps_ogc_processes_api.models.status_info import StatusInfo class ProcessesApiImpl(BaseProcessesApi): - def get_process_description(self, processId: str) -> Process: - return Process( - id=processId, - title="Sample Process", - description="This is a sample process description", - version="1.0.0", - jobControlOptions=["sync-execute", "async-execute"], - outputTransmission=["value"], - executeEndpoint=f"http://example.com/api/processes/{processId}/execution", - inputs={}, - outputs={}, - links=[ - Link( - href=f"http://example.com/api/processes/{processId}", - rel="self", - type="application/json", - title="This process", - ) - ], + def __init__( + self, settings: Settings, redis_locking_client: RedisLock, db: Session + ): + self.settings = settings + self.redis_locking_client = redis_locking_client + self.db = db + self.ems_api_auth = HTTPBasicAuth( + settings.EMS_API_AUTH_USERNAME, + settings.EMS_API_AUTH_PASSWORD.get_secret_value(), ) + def get_process_description(self, processId: str) -> Process: + process = crud.get_process(self.db, processId) + return Process.model_validate(process) + def get_processes(self) -> ProcessList: + processes = crud.get_processes(self.db) return ProcessList( processes=[ ProcessSummary( - id="process1", - title="Process 1", - description="Description 1", - version="1.0.0", - ), - ProcessSummary( - id="process2", - title="Process 2", - description="Description 2", - version="1.0.0", - ), - ], - links=[ - Link( - href="http://example.com/api/processes", - rel="self", - type="application/json", - title="this document", + id=process.id, + title=process.title, + description=process.description, + version=process.version, ) + for process in processes ], + links=[], ) def execute( @@ -63,20 +58,57 @@ def execute( response: str, prefer: str, ) -> Execute200Response: - # Placeholder implementation - # In a real-world scenario, you would execute the process here - # and return the appropriate response based on the execution mode + self.check_process_integrity(processId, new_process=False) - if prefer == "respond-async": - # Asynchronous execution - return StatusInfo( + job_id = str(uuid.uuid4()) + logical_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") + data = { + "dag_run_id": job_id, + "logical_date": logical_date, + "conf": execute_workflows.model_dump(), + } + + try: + airflow_response = requests.post( + f"{self.settings.EMS_API_URL}/dags/{processId}/dagRuns", + json=data, + auth=self.ems_api_auth, + ) + airflow_response.raise_for_status() + + job = StatusInfo( + jobID=job_id, + processID=processId, type="process", - job_id="sample_job_id", - status="accepted", - message="Process execution started asynchronously", + status=StatusCode.ACCEPTED, + created=datetime.now(), + updated=datetime.now(), ) - else: - # Synchronous execution - return Execute200Response( - outputs={"result": "Sample output for synchronous execution"} + crud.create_job(self.db, job.model_dump()) + + if prefer == "respond-async": + # Asynchronous execution + return StatusInfo( + type="process", + job_id=job_id, + status=StatusCode.ACCEPTED, + message="Process execution started asynchronously", + ) + else: + # Synchronous execution + # Note: In a real-world scenario, you'd wait for the job to complete + # and return the actual results. This is a simplified version. + return Execute200Response( + outputs={"result": "Sample output for synchronous execution"} + ) + + except requests.exceptions.RequestException as e: + status_code_to_raise = fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR + detail_message = ( + f"Failed to start DAG run {job_id} with DAG {processId}: {str(e)}" ) + + if hasattr(e, "response"): + detail_message = f"Failed to start DAG run {job_id} with DAG {processId}: {e.response.status_code} {e.response.reason}" + + raise HTTPException(status_code=status_code_to_raise, detail=detail_message) diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api.py index 98c8c5f..2903053 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api.py @@ -4,10 +4,18 @@ import pkgutil from typing import Dict -from fastapi import APIRouter, Header, Path +from fastapi import APIRouter, Depends, Header, Path +from sqlalchemy.orm import Session import openapi_server.impl +from openapi_server.config.config import Settings +from openapi_server.utils.redis import RedisLock from unity_sps_ogc_processes_api.apis.jobs_api_base import BaseJobsApi +from unity_sps_ogc_processes_api.dependencies import ( + get_db, + get_redis_locking_client, + get_settings, +) from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.extra_models import TokenModel # noqa: F401 from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData @@ -36,10 +44,14 @@ response_model_by_alias=True, ) async def dismiss( + settings: Settings = Depends(get_settings), + redis_locking_client: RedisLock = Depends(get_redis_locking_client), + db: Session = Depends(get_db), jobId: str = Path(..., description="local identifier of a job"), ) -> StatusInfo: """Cancel a job execution and remove it from the jobs list. For more information, see [Section 14]https://docs.ogc.org/is/18-062r2/18-062r2.html#Dismiss).""" - return BaseJobsApi.subclasses[0]().dismiss(jobId) + jobs_api = BaseJobsApi.subclasses[0](settings, redis_locking_client, db) + return jobs_api.dismiss(jobId) @router.get( @@ -55,9 +67,14 @@ async def dismiss( summary="retrieve the list of jobs.", response_model_by_alias=True, ) -async def get_jobs() -> JobList: +async def get_jobs( + settings: Settings = Depends(get_settings), + redis_locking_client: RedisLock = Depends(get_redis_locking_client), + db: Session = Depends(get_db), +) -> JobList: """Lists available jobs. For more information, see [Section 12](https://docs.ogc.org/is/18-062r2/18-062r2.html#Job_list).""" - return BaseJobsApi.subclasses[0]().get_jobs() + jobs_api = BaseJobsApi.subclasses[0](settings, redis_locking_client, db) + return jobs_api.get_jobs() @router.get( @@ -78,6 +95,9 @@ async def get_jobs() -> JobList: response_model_by_alias=True, ) async def get_result( + settings: Settings = Depends(get_settings), + redis_locking_client: RedisLock = Depends(get_redis_locking_client), + db: Session = Depends(get_db), jobId: str = Path(..., description="local identifier of a job"), prefer: str = Header( None, @@ -85,7 +105,8 @@ async def get_result( ), ) -> Dict[str, InlineOrRefData]: """Lists available results of a job. In case of a failure, lists exceptions instead. For more information, see [Section 7.11](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_retrieve_job_results).""" - return BaseJobsApi.subclasses[0]().get_result(jobId, prefer) + jobs_api = BaseJobsApi.subclasses[0](settings, redis_locking_client, db) + return jobs_api.get_result(jobId, prefer) @router.get( @@ -103,7 +124,11 @@ async def get_result( response_model_by_alias=True, ) async def get_status( + settings: Settings = Depends(get_settings), + redis_locking_client: RedisLock = Depends(get_redis_locking_client), + db: Session = Depends(get_db), jobId: str = Path(..., description="local identifier of a job"), ) -> StatusInfo: """Shows the status of a job. For more information, see [Section 7.10](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_retrieve_status_info).""" - return BaseJobsApi.subclasses[0]().get_status(jobId) + jobs_api = BaseJobsApi.subclasses[0](settings, redis_locking_client, db) + return jobs_api.get_status(jobId) diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api.py index b06465a..8354861 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api.py @@ -3,10 +3,18 @@ import importlib import pkgutil -from fastapi import APIRouter, Body, Header, Path, Query +from fastapi import APIRouter, Body, Depends, Header, Path, Query +from sqlalchemy.orm import Session import openapi_server.impl +from openapi_server.config.config import Settings +from openapi_server.utils.redis import RedisLock from unity_sps_ogc_processes_api.apis.processes_api_base import BaseProcessesApi +from unity_sps_ogc_processes_api.dependencies import ( + get_db, + get_redis_locking_client, + get_settings, +) from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows @@ -46,6 +54,9 @@ response_model_by_alias=True, ) async def execute( + settings: Settings = Depends(get_settings), + redis_locking_client: RedisLock = Depends(get_redis_locking_client), + db: Session = Depends(get_db), processId: str = Path(..., description=""), execute_workflows: ExecuteWorkflows = Body( None, @@ -62,9 +73,8 @@ async def execute( ), ) -> Execute200Response: """Executes a process (this may result in the creation of a job resource e.g., for _asynchronous execution_). For more information, see [Section 7.9](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_create_job).""" - return BaseProcessesApi.subclasses[0]().execute( - processId, execute_workflows, response, prefer - ) + processes_api = BaseProcessesApi.subclasses[0](settings, redis_locking_client, db) + return processes_api.execute(processId, execute_workflows, response, prefer) @router.get( @@ -81,10 +91,14 @@ async def execute( response_model_by_alias=True, ) async def get_process_description( + settings: Settings = Depends(get_settings), + redis_locking_client: RedisLock = Depends(get_redis_locking_client), + db: Session = Depends(get_db), processId: str = Path(..., description=""), ) -> Process: """The process description contains information about inputs and outputs and a link to the execution-endpoint for the process. The Core does not mandate the use of a specific process description to specify the interface of a process. That said, the Core requirements class makes the following recommendation: Implementations SHOULD consider supporting the OGC process description. For more information, see [Section 7.8](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_process_description).""" - return BaseProcessesApi.subclasses[0]().get_process_description(processId) + processes_api = BaseProcessesApi.subclasses[0](settings, redis_locking_client, db) + return processes_api.get_process_description(processId) @router.get( @@ -99,6 +113,11 @@ async def get_process_description( summary="retrieve the list of available processes", response_model_by_alias=True, ) -async def get_processes() -> ProcessList: +async def get_processes( + settings: Settings = Depends(get_settings), + redis_locking_client: RedisLock = Depends(get_redis_locking_client), + db: Session = Depends(get_db), +) -> ProcessList: """The list of processes contains a summary of each process the OGC API - Processes offers, including the link to a more detailed description of the process. For more information, see [Section 7.7]https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_process_list).""" - return BaseProcessesApi.subclasses[0]().get_processes() + processes_api = BaseProcessesApi.subclasses[0](settings, redis_locking_client, db) + return processes_api.get_processes() From f5329dee929593e9bc16cab1ac913fe6b1d886dd Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 3 Sep 2024 14:02:51 -0400 Subject: [PATCH 22/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/jobs_api.py | 32 +++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/app-cp/src/openapi_server/impl/jobs_api.py b/app-cp/src/openapi_server/impl/jobs_api.py index e59a2b0..568ddac 100644 --- a/app-cp/src/openapi_server/impl/jobs_api.py +++ b/app-cp/src/openapi_server/impl/jobs_api.py @@ -5,11 +5,13 @@ import requests from fastapi import HTTPException from fastapi import status as fastapi_status +from jsonschema import ValidationError, validate from requests.auth import HTTPBasicAuth from sqlalchemy.orm import Session from openapi_server.config.config import Settings from openapi_server.database import crud +from openapi_server.impl.processes_api import ProcessesApiImpl from openapi_server.utils.redis import RedisLock from unity_sps_ogc_processes_api.apis.jobs_api_base import BaseJobsApi from unity_sps_ogc_processes_api.models.execute import Execute @@ -125,12 +127,40 @@ def get_status(self, jobId: str) -> StatusInfo: return crud.update_job(self.db, job) def execute(self, processId: str, execute: Execute) -> StatusInfo: + # Fetch process description + processes_api = ProcessesApiImpl( + self.settings, self.redis_locking_client, self.db + ) + process_description = processes_api.get_process(processId) + + # Validate inputs against schema + validated_inputs = {} + for input_id, input_value in execute.inputs.items(): + input_description = next( + (input for input in process_description.inputs if input.id == input_id), + None, + ) + if input_description is None: + raise HTTPException( + status_code=fastapi_status.HTTP_400_BAD_REQUEST, + detail=f"Invalid input: {input_id}", + ) + + try: + validate(instance=input_value.value, schema=input_description.schema_) + validated_inputs[input_id] = input_value.value + except ValidationError as e: + raise HTTPException( + status_code=fastapi_status.HTTP_400_BAD_REQUEST, + detail=f"Invalid input for {input_id}: {e.message}", + ) + job_id = str(uuid.uuid4()) logical_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") data = { "dag_run_id": job_id, "logical_date": logical_date, - "conf": execute.model_dump(), + "conf": validated_inputs, } try: From e0ca255f988d70206ce6789976bc79eadcdeb81d Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 3 Sep 2024 14:16:56 -0400 Subject: [PATCH 23/60] feat: Initial autogenerated FastAPI implementation --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e615058..f1531e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,8 @@ dependencies = [ "uvicorn==0.27.1", "psycopg2-binary==2.9.9", "redis==5.0.4", - "apache-airflow-client @ git+https://github.com/apache/airflow-client-python.git@2.9.0" + "apache-airflow-client @ git+https://github.com/apache/airflow-client-python.git@2.9.0", + "jsonschema==4.23.0" ] [project.license] From 97c0737ca3bff48d6a277d2305116960bd8851d1 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 3 Sep 2024 14:26:33 -0400 Subject: [PATCH 24/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/jobs_api.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/app-cp/src/openapi_server/impl/jobs_api.py b/app-cp/src/openapi_server/impl/jobs_api.py index 568ddac..3a5c8c8 100644 --- a/app-cp/src/openapi_server/impl/jobs_api.py +++ b/app-cp/src/openapi_server/impl/jobs_api.py @@ -5,7 +5,8 @@ import requests from fastapi import HTTPException from fastapi import status as fastapi_status -from jsonschema import ValidationError, validate + +# from jsonschema import ValidationError, validate from requests.auth import HTTPBasicAuth from sqlalchemy.orm import Session @@ -146,14 +147,15 @@ def execute(self, processId: str, execute: Execute) -> StatusInfo: detail=f"Invalid input: {input_id}", ) - try: - validate(instance=input_value.value, schema=input_description.schema_) - validated_inputs[input_id] = input_value.value - except ValidationError as e: - raise HTTPException( - status_code=fastapi_status.HTTP_400_BAD_REQUEST, - detail=f"Invalid input for {input_id}: {e.message}", - ) + # try: + # #validate(instance=input_value.value, schema=input_description.schema_) + # validated_inputs[input_id] = input_value.value + # except ValidationError as e: + # raise HTTPException( + # status_code=fastapi_status.HTTP_400_BAD_REQUEST, + # detail=f"Invalid input for {input_id}: {e.message}", + # ) + validated_inputs[input_id] = input_value.value job_id = str(uuid.uuid4()) logical_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") From f3a8011117271a25142d296af67bb4aa4918fc9d Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 3 Sep 2024 14:50:08 -0400 Subject: [PATCH 25/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/processes_api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index 2943b85..178b02a 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -9,6 +9,7 @@ from openapi_server.config.config import Settings from openapi_server.database import crud +from openapi_server.impl.dru_api import check_process_integrity from openapi_server.utils.redis import RedisLock from unity_sps_ogc_processes_api.apis.processes_api_base import BaseProcessesApi from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response @@ -58,7 +59,7 @@ def execute( response: str, prefer: str, ) -> Execute200Response: - self.check_process_integrity(processId, new_process=False) + check_process_integrity(processId, new_process=False) job_id = str(uuid.uuid4()) logical_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") From c1825d446701789b9c610d120f09aa2ce21d2063 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 3 Sep 2024 14:56:03 -0400 Subject: [PATCH 26/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/processes_api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index 178b02a..8aaf357 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -59,8 +59,7 @@ def execute( response: str, prefer: str, ) -> Execute200Response: - check_process_integrity(processId, new_process=False) - + check_process_integrity(self.db, processId, new_process=True) job_id = str(uuid.uuid4()) logical_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") data = { From c2d3025478621990543faf66e9010a5b102a8cd6 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 3 Sep 2024 15:02:58 -0400 Subject: [PATCH 27/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/processes_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index 8aaf357..fabd06d 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -59,7 +59,7 @@ def execute( response: str, prefer: str, ) -> Execute200Response: - check_process_integrity(self.db, processId, new_process=True) + check_process_integrity(self.db, processId, new_process=False) job_id = str(uuid.uuid4()) logical_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") data = { From cbaeba5ae0dae3e46a4e5669ca7ec60f95198056 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 3 Sep 2024 15:17:49 -0400 Subject: [PATCH 28/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/jobs_api.py | 64 ------------------- .../src/openapi_server/impl/processes_api.py | 31 ++++++++- 2 files changed, 30 insertions(+), 65 deletions(-) diff --git a/app-cp/src/openapi_server/impl/jobs_api.py b/app-cp/src/openapi_server/impl/jobs_api.py index 3a5c8c8..edd4951 100644 --- a/app-cp/src/openapi_server/impl/jobs_api.py +++ b/app-cp/src/openapi_server/impl/jobs_api.py @@ -1,4 +1,3 @@ -import uuid from datetime import datetime from typing import Dict @@ -12,10 +11,8 @@ from openapi_server.config.config import Settings from openapi_server.database import crud -from openapi_server.impl.processes_api import ProcessesApiImpl from openapi_server.utils.redis import RedisLock from unity_sps_ogc_processes_api.apis.jobs_api_base import BaseJobsApi -from unity_sps_ogc_processes_api.models.execute import Execute from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData from unity_sps_ogc_processes_api.models.job_list import JobList from unity_sps_ogc_processes_api.models.status_code import StatusCode @@ -126,64 +123,3 @@ def get_status(self, jobId: str) -> StatusInfo: job.finished = datetime.fromisoformat(end_date_str) return crud.update_job(self.db, job) - - def execute(self, processId: str, execute: Execute) -> StatusInfo: - # Fetch process description - processes_api = ProcessesApiImpl( - self.settings, self.redis_locking_client, self.db - ) - process_description = processes_api.get_process(processId) - - # Validate inputs against schema - validated_inputs = {} - for input_id, input_value in execute.inputs.items(): - input_description = next( - (input for input in process_description.inputs if input.id == input_id), - None, - ) - if input_description is None: - raise HTTPException( - status_code=fastapi_status.HTTP_400_BAD_REQUEST, - detail=f"Invalid input: {input_id}", - ) - - # try: - # #validate(instance=input_value.value, schema=input_description.schema_) - # validated_inputs[input_id] = input_value.value - # except ValidationError as e: - # raise HTTPException( - # status_code=fastapi_status.HTTP_400_BAD_REQUEST, - # detail=f"Invalid input for {input_id}: {e.message}", - # ) - validated_inputs[input_id] = input_value.value - - job_id = str(uuid.uuid4()) - logical_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") - data = { - "dag_run_id": job_id, - "logical_date": logical_date, - "conf": validated_inputs, - } - - try: - response = requests.post( - f"{self.settings.EMS_API_URL}/dags/{processId}/dagRuns", - json=data, - auth=self.ems_api_auth, - ) - response.raise_for_status() - self.check_job_integrity(job_id, new_job=True) - job = StatusInfo( - jobID=job_id, - processID=processId, - type="process", - status=StatusCode.ACCEPTED, - created=datetime.now(), - updated=datetime.now(), - ) - return crud.create_job(self.db, job) - except requests.exceptions.RequestException as e: - raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to start DAG run {job_id} with DAG {processId}: {e}", - ) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index fabd06d..f051c41 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -60,12 +60,41 @@ def execute( prefer: str, ) -> Execute200Response: check_process_integrity(self.db, processId, new_process=False) + # Fetch process description + processes_api = ProcessesApiImpl( + self.settings, self.redis_locking_client, self.db + ) + process_description = processes_api.get_process(processId) + + # Validate inputs against schema + validated_inputs = {} + for input_id, input_value in execute_workflows.inputs.items(): + input_description = next( + (input for input in process_description.inputs if input.id == input_id), + None, + ) + if input_description is None: + raise HTTPException( + status_code=fastapi_status.HTTP_400_BAD_REQUEST, + detail=f"Invalid input: {input_id}", + ) + + # try: + # #validate(instance=input_value.value, schema=input_description.schema_) + # validated_inputs[input_id] = input_value.value + # except ValidationError as e: + # raise HTTPException( + # status_code=fastapi_status.HTTP_400_BAD_REQUEST, + # detail=f"Invalid input for {input_id}: {e.message}", + # ) + validated_inputs[input_id] = input_value.value + job_id = str(uuid.uuid4()) logical_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") data = { "dag_run_id": job_id, "logical_date": logical_date, - "conf": execute_workflows.model_dump(), + "conf": validated_inputs, } try: From e73ecf047d819097df524b9027f797a69d4ab0b6 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 3 Sep 2024 15:24:41 -0400 Subject: [PATCH 29/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/processes_api.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index f051c41..dd37b94 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -61,10 +61,7 @@ def execute( ) -> Execute200Response: check_process_integrity(self.db, processId, new_process=False) # Fetch process description - processes_api = ProcessesApiImpl( - self.settings, self.redis_locking_client, self.db - ) - process_description = processes_api.get_process(processId) + process_description = self.get_process_description(processId) # Validate inputs against schema validated_inputs = {} From 30efac8dd502ad907e79b9e4e234f83d8f7d978a Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 3 Sep 2024 16:06:35 -0400 Subject: [PATCH 30/60] feat: Initial autogenerated FastAPI implementation --- .../src/openapi_server/impl/processes_api.py | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index dd37b94..f108b61 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -14,6 +14,10 @@ from unity_sps_ogc_processes_api.apis.processes_api_base import BaseProcessesApi from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows +from unity_sps_ogc_processes_api.models.input_description import InputDescription +from unity_sps_ogc_processes_api.models.link import Link +from unity_sps_ogc_processes_api.models.metadata import Metadata +from unity_sps_ogc_processes_api.models.output_description import OutputDescription from unity_sps_ogc_processes_api.models.process import Process from unity_sps_ogc_processes_api.models.process_list import ProcessList from unity_sps_ogc_processes_api.models.process_summary import ProcessSummary @@ -35,7 +39,41 @@ def __init__( def get_process_description(self, processId: str) -> Process: process = crud.get_process(self.db, processId) - return Process.model_validate(process) + + # Convert metadata, links, inputs, and outputs if they exist + metadata = ( + [Metadata.model_validate(m) for m in process.metadata] + if process.metadata + else None + ) + links = ( + [Link.model_validate(link) for link in process.links] + if process.links + else None + ) + inputs = ( + {k: InputDescription.model_validate(v) for k, v in process.inputs.items()} + if process.inputs + else None + ) + outputs = ( + {k: OutputDescription.model_validate(v) for k, v in process.outputs.items()} + if process.outputs + else None + ) + + return Process( + title=process.title, + description=process.description, + keywords=process.keywords, + metadata=metadata, + id=process.id, + version=process.version, + job_control_options=process.job_control_options, + links=links, + inputs=inputs, + outputs=outputs, + ) def get_processes(self) -> ProcessList: processes = crud.get_processes(self.db) From cbf62b120c9797df9029db043fb371697da1e936 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 3 Sep 2024 16:17:02 -0400 Subject: [PATCH 31/60] feat: Initial autogenerated FastAPI implementation --- .../src/openapi_server/impl/processes_api.py | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index f108b61..2581769 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -99,37 +99,41 @@ def execute( ) -> Execute200Response: check_process_integrity(self.db, processId, new_process=False) # Fetch process description - process_description = self.get_process_description(processId) + # process_description = self.get_process_description(processId) # Validate inputs against schema - validated_inputs = {} - for input_id, input_value in execute_workflows.inputs.items(): - input_description = next( - (input for input in process_description.inputs if input.id == input_id), - None, - ) - if input_description is None: - raise HTTPException( - status_code=fastapi_status.HTTP_400_BAD_REQUEST, - detail=f"Invalid input: {input_id}", - ) - - # try: - # #validate(instance=input_value.value, schema=input_description.schema_) - # validated_inputs[input_id] = input_value.value - # except ValidationError as e: - # raise HTTPException( - # status_code=fastapi_status.HTTP_400_BAD_REQUEST, - # detail=f"Invalid input for {input_id}: {e.message}", - # ) - validated_inputs[input_id] = input_value.value + # validated_inputs = {} + # for input_id, input_value in execute_workflows.inputs.items(): + # input_description = next( + # (input for input in process_description.inputs if input.id == input_id), + # None, + # ) + # if input_description is None: + # raise HTTPException( + # status_code=fastapi_status.HTTP_400_BAD_REQUEST, + # detail=f"Invalid input: {input_id}", + # ) + + # try: + # #validate(instance=input_value.value, schema=input_description.schema_) + # validated_inputs[input_id] = input_value.value + # except ValidationError as e: + # raise HTTPException( + # status_code=fastapi_status.HTTP_400_BAD_REQUEST, + # detail=f"Invalid input for {input_id}: {e.message}", + # ) + # validated_inputs[input_id] = input_value.value job_id = str(uuid.uuid4()) logical_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") data = { "dag_run_id": job_id, "logical_date": logical_date, - "conf": validated_inputs, + "conf": ( + execute_workflows.inputs.model_dump() + if execute_workflows.inputs + else {} + ), } try: From 7c860ad7107ddd130a1e88309551302b74891673 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 3 Sep 2024 16:22:21 -0400 Subject: [PATCH 32/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/processes_api.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index 2581769..8dfdf69 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -129,11 +129,7 @@ def execute( data = { "dag_run_id": job_id, "logical_date": logical_date, - "conf": ( - execute_workflows.inputs.model_dump() - if execute_workflows.inputs - else {} - ), + "conf": execute_workflows.inputs, } try: From 1085142f09204c420e7605b7960891b5d647f0a9 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Wed, 4 Sep 2024 09:25:05 -0400 Subject: [PATCH 33/60] feat: Initial autogenerated FastAPI implementation --- .../src/openapi_server/impl/processes_api.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index 8dfdf69..d178325 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -15,6 +15,7 @@ from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows from unity_sps_ogc_processes_api.models.input_description import InputDescription +from unity_sps_ogc_processes_api.models.input_workflows import InputWorkflows from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.metadata import Metadata from unity_sps_ogc_processes_api.models.output_description import OutputDescription @@ -123,15 +124,29 @@ def execute( # detail=f"Invalid input for {input_id}: {e.message}", # ) # validated_inputs[input_id] = input_value.value - job_id = str(uuid.uuid4()) logical_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") + + inputs_dict = {} + if execute_workflows.inputs: + for key, value in execute_workflows.inputs.items(): + if isinstance(value, InputWorkflows): + inputs_dict[key] = value.model_dump(exclude_unset=True) + else: + inputs_dict[key] = value + data = { "dag_run_id": job_id, "logical_date": logical_date, - "conf": execute_workflows.inputs, + "conf": inputs_dict, } + # data = { + # "dag_run_id": job_id, + # "logical_date": logical_date, + # "conf": execute_workflows.inputs, + # } + try: airflow_response = requests.post( f"{self.settings.EMS_API_URL}/dags/{processId}/dagRuns", From dcdaa421cf0777e684e7e1928c526a37df6ebc26 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Wed, 4 Sep 2024 09:37:22 -0400 Subject: [PATCH 34/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/processes_api.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index d178325..e8c58e3 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -15,7 +15,6 @@ from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows from unity_sps_ogc_processes_api.models.input_description import InputDescription -from unity_sps_ogc_processes_api.models.input_workflows import InputWorkflows from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.metadata import Metadata from unity_sps_ogc_processes_api.models.output_description import OutputDescription @@ -130,9 +129,9 @@ def execute( inputs_dict = {} if execute_workflows.inputs: for key, value in execute_workflows.inputs.items(): - if isinstance(value, InputWorkflows): + try: inputs_dict[key] = value.model_dump(exclude_unset=True) - else: + except Exception: inputs_dict[key] = value data = { From be921226314934cd1afdf7fc256e4bae68052dfa Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Wed, 4 Sep 2024 09:49:55 -0400 Subject: [PATCH 35/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/processes_api.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index e8c58e3..286c94f 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -98,9 +98,9 @@ def execute( prefer: str, ) -> Execute200Response: check_process_integrity(self.db, processId, new_process=False) + # Fetch process description # process_description = self.get_process_description(processId) - # Validate inputs against schema # validated_inputs = {} # for input_id, input_value in execute_workflows.inputs.items(): @@ -113,7 +113,6 @@ def execute( # status_code=fastapi_status.HTTP_400_BAD_REQUEST, # detail=f"Invalid input: {input_id}", # ) - # try: # #validate(instance=input_value.value, schema=input_description.schema_) # validated_inputs[input_id] = input_value.value @@ -123,6 +122,7 @@ def execute( # detail=f"Invalid input for {input_id}: {e.message}", # ) # validated_inputs[input_id] = input_value.value + job_id = str(uuid.uuid4()) logical_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") @@ -140,12 +140,6 @@ def execute( "conf": inputs_dict, } - # data = { - # "dag_run_id": job_id, - # "logical_date": logical_date, - # "conf": execute_workflows.inputs, - # } - try: airflow_response = requests.post( f"{self.settings.EMS_API_URL}/dags/{processId}/dagRuns", @@ -162,7 +156,7 @@ def execute( created=datetime.now(), updated=datetime.now(), ) - crud.create_job(self.db, job.model_dump()) + crud.create_job(self.db, job.model_dump(by_alias=True)) if prefer == "respond-async": # Asynchronous execution From 3bf6c8af006e9177bf03bf2ea31522dca94f4f2f Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Wed, 4 Sep 2024 10:03:19 -0400 Subject: [PATCH 36/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/processes_api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index 286c94f..a4ae779 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -157,7 +157,6 @@ def execute( updated=datetime.now(), ) crud.create_job(self.db, job.model_dump(by_alias=True)) - if prefer == "respond-async": # Asynchronous execution return StatusInfo( @@ -171,7 +170,7 @@ def execute( # Note: In a real-world scenario, you'd wait for the job to complete # and return the actual results. This is a simplified version. return Execute200Response( - outputs={"result": "Sample output for synchronous execution"} + {"result": "Sample output for synchronous execution"} ) except requests.exceptions.RequestException as e: From f7559d692995a02527db19418eb3361e9955547b Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Wed, 4 Sep 2024 10:17:33 -0400 Subject: [PATCH 37/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/processes_api.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index a4ae779..f769bb0 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -149,9 +149,9 @@ def execute( airflow_response.raise_for_status() job = StatusInfo( - jobID=job_id, processID=processId, type="process", + jobID=job_id, status=StatusCode.ACCEPTED, created=datetime.now(), updated=datetime.now(), @@ -159,12 +159,7 @@ def execute( crud.create_job(self.db, job.model_dump(by_alias=True)) if prefer == "respond-async": # Asynchronous execution - return StatusInfo( - type="process", - job_id=job_id, - status=StatusCode.ACCEPTED, - message="Process execution started asynchronously", - ) + return job else: # Synchronous execution # Note: In a real-world scenario, you'd wait for the job to complete From 2e69b4599266d61c14fe3b5d0d4e903317fb9e91 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Wed, 4 Sep 2024 15:42:06 -0400 Subject: [PATCH 38/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/jobs_api.py | 44 +++++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/app-cp/src/openapi_server/impl/jobs_api.py b/app-cp/src/openapi_server/impl/jobs_api.py index edd4951..5c5c540 100644 --- a/app-cp/src/openapi_server/impl/jobs_api.py +++ b/app-cp/src/openapi_server/impl/jobs_api.py @@ -69,17 +69,39 @@ def dismiss(self, jobId: str) -> StatusInfo: ) crud.delete_job(self.db, job) + dismissed_datetime = datetime.now() return StatusInfo( - type="process", - job_id=jobId, + process_id=job.processID, + type=job.type, + job_id=job.jobID, status=StatusCode.DISMISSED, message="Job dismissed", - updated=datetime.now(), + updated=dismissed_datetime, + created=job.created, + started=job.started, + finished=dismissed_datetime, + progress=job.progress, + links=job.links, ) def get_jobs(self) -> JobList: jobs = crud.get_jobs(self.db) - job_status_infos = [StatusInfo.model_validate(job) for job in jobs] + job_status_infos = [ + StatusInfo( + process_id=job.processID, + type=job.type, + job_id=job.jobID, + status=job.status, + message=job.message, + updated=job.updated, + created=job.created, + started=job.started, + finished=job.finished, + progress=job.progress, + links=job.links, + ) + for job in jobs + ] return JobList( jobs=job_status_infos, links=[], @@ -92,7 +114,19 @@ def get_result(self, jobId: str, prefer: str) -> Dict[str, InlineOrRefData]: def get_status(self, jobId: str) -> StatusInfo: job = self.check_job_integrity(jobId, new_job=False) - job = StatusInfo.model_validate(job) + job = StatusInfo( + process_id=job.processID, + type=job.type, + job_id=job.jobID, + status=job.status, + message=job.message, + updated=job.updated, + created=job.created, + started=job.started, + finished=job.finished, + progress=job.progress, + links=job.links, + ) try: response = requests.get( From d9ac14a211b09cf61505ed2fa20f38977df54c59 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Wed, 4 Sep 2024 15:54:29 -0400 Subject: [PATCH 39/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/jobs_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app-cp/src/openapi_server/impl/jobs_api.py b/app-cp/src/openapi_server/impl/jobs_api.py index 5c5c540..deae71e 100644 --- a/app-cp/src/openapi_server/impl/jobs_api.py +++ b/app-cp/src/openapi_server/impl/jobs_api.py @@ -130,14 +130,14 @@ def get_status(self, jobId: str) -> StatusInfo: try: response = requests.get( - f"{self.settings.EMS_API_URL}/dags/{job.processID}/dagRuns/{job.jobID}", + f"{self.settings.EMS_API_URL}/dags/{job.process_id}/dagRuns/{job.job_id}", auth=self.ems_api_auth, ) response.raise_for_status() except requests.exceptions.HTTPError as e: raise HTTPException( status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to fetch DAG run {job.jobID} for DAG {job.processID}: {e}", + detail=f"Failed to fetch DAG run {job.job_id} for DAG {job.process_id}: {e}", ) execution_status_conversion_dict = { From d8763b729102383b33d9e3a47d275d4a1a46c834 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Wed, 4 Sep 2024 16:03:10 -0400 Subject: [PATCH 40/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/jobs_api.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app-cp/src/openapi_server/impl/jobs_api.py b/app-cp/src/openapi_server/impl/jobs_api.py index deae71e..fc3a922 100644 --- a/app-cp/src/openapi_server/impl/jobs_api.py +++ b/app-cp/src/openapi_server/impl/jobs_api.py @@ -8,6 +8,7 @@ # from jsonschema import ValidationError, validate from requests.auth import HTTPBasicAuth from sqlalchemy.orm import Session +from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound from openapi_server.config.config import Settings from openapi_server.database import crud @@ -36,13 +37,13 @@ def check_job_integrity(self, job_id: str, new_job: bool): job = crud.get_job(self.db, job_id) if new_job and job is not None: raise ValueError - except crud.NoResultFound: + except NoResultFound: if not new_job: raise HTTPException( status_code=fastapi_status.HTTP_404_NOT_FOUND, detail=f"Job with ID '{job_id}' not found", ) - except crud.MultipleResultsFound: + except MultipleResultsFound: raise HTTPException( status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Multiple jobs found with same ID '{job_id}', data integrity error", From 91f2421fe01634c905ff46c71c34ec5e886912c5 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Wed, 4 Sep 2024 16:12:35 -0400 Subject: [PATCH 41/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/jobs_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-cp/src/openapi_server/impl/jobs_api.py b/app-cp/src/openapi_server/impl/jobs_api.py index fc3a922..70d1577 100644 --- a/app-cp/src/openapi_server/impl/jobs_api.py +++ b/app-cp/src/openapi_server/impl/jobs_api.py @@ -157,4 +157,4 @@ def get_status(self, jobId: str) -> StatusInfo: if end_date_str: job.finished = datetime.fromisoformat(end_date_str) - return crud.update_job(self.db, job) + return crud.update_job(self.db, job.job_id, job.model_dump(by_alias=True)) From de0a47c5ff9ecdc5b7c60509e218c72102aca7b1 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 5 Sep 2024 15:33:53 -0400 Subject: [PATCH 42/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/dru_api.py | 51 +++- app-cp/src/openapi_server/impl/jobs_api.py | 171 ++++++++----- .../src/openapi_server/impl/processes_api.py | 241 ++++++++++-------- 3 files changed, 285 insertions(+), 178 deletions(-) diff --git a/app-cp/src/openapi_server/impl/dru_api.py b/app-cp/src/openapi_server/impl/dru_api.py index 25abe90..076538e 100644 --- a/app-cp/src/openapi_server/impl/dru_api.py +++ b/app-cp/src/openapi_server/impl/dru_api.py @@ -3,7 +3,9 @@ import time import requests -from fastapi import HTTPException, Response, status +from fastapi import HTTPException, Response +from fastapi import status +from fastapi import status as fastapi_status from requests.auth import HTTPBasicAuth from sqlalchemy.orm import Session from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound @@ -49,13 +51,12 @@ def __init__( self.db = db def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: - check_process_integrity( - self.db, ogcapppkg.process_description.id, new_process=True - ) + lock_key = f"process:{ogcapppkg.process_description.id}" try: - with self.redis_locking_client.lock( - "deploy_process_" + ogcapppkg.process_description.id - ): + with self.redis_locking_client.lock(lock_key, expire=60): + check_process_integrity( + self.db, ogcapppkg.process_description.id, new_process=True + ) # ogcapppkg.process_description.deployment_status = "deploying" crud.create_process(self.db, ogcapppkg) @@ -130,12 +131,21 @@ def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: status_code=status.HTTP_201_CREATED, content=f"Process {ogcapppkg.process_description.id} deployed successfully", ) + except self.redis_locking_client.LockError: + raise HTTPException( + status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + detail="Unable to acquire lock. Please try again later.", + ) except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException( + status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: + lock_key = f"process:{processId}" try: - with self.redis_locking_client.lock(f"replace_process_{processId}"): + with self.redis_locking_client.lock(lock_key): + check_process_integrity(self.db, processId, new_process=False) # Validate the new ogcapppkg if ogcapppkg.process_description.id != processId: raise HTTPException( @@ -181,13 +191,21 @@ def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: status_code=status.HTTP_204_NO_CONTENT, content=f"Process {processId} replaced successfully", ) + except self.redis_locking_client.LockError: + raise HTTPException( + status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + detail="Unable to acquire lock. Please try again later.", + ) except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException( + status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) def undeploy(self, processId: str) -> None: - check_process_integrity(self.db, processId, new_process=False) + lock_key = f"process:{processId}" try: - with self.redis_locking_client.lock(f"undeploy_process_{processId}"): + with self.redis_locking_client.lock(lock_key): + check_process_integrity(self.db, processId, new_process=False) # Remove the DAG file from the deployed directory dag_filename = f"{processId}.py" deployed_dag_path = os.path.join( @@ -214,5 +232,12 @@ def undeploy(self, processId: str) -> None: status_code=status.HTTP_204_NO_CONTENT, content=f"Process {processId} undeployed successfully", ) + except self.redis_locking_client.LockError: + raise HTTPException( + status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + detail="Unable to acquire lock. Please try again later.", + ) except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise HTTPException( + status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) diff --git a/app-cp/src/openapi_server/impl/jobs_api.py b/app-cp/src/openapi_server/impl/jobs_api.py index 70d1577..e258a07 100644 --- a/app-cp/src/openapi_server/impl/jobs_api.py +++ b/app-cp/src/openapi_server/impl/jobs_api.py @@ -56,34 +56,47 @@ def check_job_integrity(self, job_id: str, new_job: bool): return job def dismiss(self, jobId: str) -> StatusInfo: - job = self.check_job_integrity(jobId, new_job=False) + job_lock_key = f"job:{jobId}" try: - response = requests.delete( - f"{self.settings.EMS_API_URL}/dags/{job.processID}/dagRuns/{job.jobID}", - auth=self.ems_api_auth, + with self.redis_locking_client.lock(job_lock_key, expire=60): + job = self.check_job_integrity(jobId, new_job=False) + process_lock_key = f"process:{job.processID}" + with self.redis_locking_client.lock(process_lock_key, expire=60): + response = requests.delete( + f"{self.settings.EMS_API_URL}/dags/{job.processID}/dagRuns/{job.jobID}", + auth=self.ems_api_auth, + ) + response.raise_for_status() + crud.delete_job(self.db, job) + dismissed_datetime = datetime.now() + return StatusInfo( + process_id=job.processID, + type=job.type, + job_id=job.jobID, + status=StatusCode.DISMISSED, + message="Job dismissed", + updated=dismissed_datetime, + created=job.created, + started=job.started, + finished=dismissed_datetime, + progress=job.progress, + links=job.links, + ) + + except self.redis_locking_client.LockError: + raise HTTPException( + status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + detail="Unable to acquire lock. Please try again later.", ) - response.raise_for_status() except requests.exceptions.HTTPError as e: raise HTTPException( status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to delete DAG run {job.jobID} for DAG {job.processID}: {e}", ) - - crud.delete_job(self.db, job) - dismissed_datetime = datetime.now() - return StatusInfo( - process_id=job.processID, - type=job.type, - job_id=job.jobID, - status=StatusCode.DISMISSED, - message="Job dismissed", - updated=dismissed_datetime, - created=job.created, - started=job.started, - finished=dismissed_datetime, - progress=job.progress, - links=job.links, - ) + except Exception as e: + raise HTTPException( + status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) def get_jobs(self) -> JobList: jobs = crud.get_jobs(self.db) @@ -109,52 +122,88 @@ def get_jobs(self) -> JobList: ) def get_result(self, jobId: str, prefer: str) -> Dict[str, InlineOrRefData]: - self.check_job_integrity(jobId, new_job=False) - results = crud.get_results(self.db, jobId) - return {result.name: InlineOrRefData(href=result.href) for result in results} + job_lock_key = f"job:{jobId}" + try: + with self.redis_locking_client.lock(job_lock_key, expire=60): + job = self.check_job_integrity(jobId, new_job=False) + process_lock_key = f"process:{job.processID}" + with self.redis_locking_client.lock(process_lock_key, expire=60): + results = crud.get_results(self.db, jobId) + return { + result.name: InlineOrRefData(href=result.href) + for result in results + } - def get_status(self, jobId: str) -> StatusInfo: - job = self.check_job_integrity(jobId, new_job=False) - job = StatusInfo( - process_id=job.processID, - type=job.type, - job_id=job.jobID, - status=job.status, - message=job.message, - updated=job.updated, - created=job.created, - started=job.started, - finished=job.finished, - progress=job.progress, - links=job.links, - ) + except self.redis_locking_client.LockError: + raise HTTPException( + status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + detail="Unable to acquire lock. Please try again later.", + ) + except Exception as e: + raise HTTPException( + status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) + def get_status(self, jobId: str) -> StatusInfo: + job_lock_key = f"job:{jobId}" try: - response = requests.get( - f"{self.settings.EMS_API_URL}/dags/{job.process_id}/dagRuns/{job.job_id}", - auth=self.ems_api_auth, + with self.redis_locking_client.lock(job_lock_key, expire=60): + job = self.check_job_integrity(jobId, new_job=False) + process_lock_key = f"process:{job.processID}" + with self.redis_locking_client.lock(process_lock_key, expire=60): + job = StatusInfo( + process_id=job.processID, + type=job.type, + job_id=job.jobID, + status=job.status, + message=job.message, + updated=job.updated, + created=job.created, + started=job.started, + finished=job.finished, + progress=job.progress, + links=job.links, + ) + + response = requests.get( + f"{self.settings.EMS_API_URL}/dags/{job.process_id}/dagRuns/{job.job_id}", + auth=self.ems_api_auth, + ) + response.raise_for_status() + + execution_status_conversion_dict = { + "queued": StatusCode.ACCEPTED, + "running": StatusCode.RUNNING, + "success": StatusCode.SUCCESSFUL, + "failed": StatusCode.FAILED, + } + data = response.json() + current_execution_status = execution_status_conversion_dict[ + data["state"] + ] + if job.status != current_execution_status: + job.status = current_execution_status + job.updated = datetime.now() + + end_date_str = data.get("end_date", None) + if end_date_str: + job.finished = datetime.fromisoformat(end_date_str) + + return crud.update_job( + self.db, job.job_id, job.model_dump(by_alias=True) + ) + + except self.redis_locking_client.LockError: + raise HTTPException( + status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + detail="Unable to acquire lock. Please try again later.", ) - response.raise_for_status() except requests.exceptions.HTTPError as e: raise HTTPException( status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to fetch DAG run {job.job_id} for DAG {job.process_id}: {e}", ) - - execution_status_conversion_dict = { - "queued": StatusCode.ACCEPTED, - "running": StatusCode.RUNNING, - "success": StatusCode.SUCCESSFUL, - "failed": StatusCode.FAILED, - } - data = response.json() - current_execution_status = execution_status_conversion_dict[data["state"]] - if job.status != current_execution_status: - job.status = current_execution_status - job.updated = datetime.now() - - end_date_str = data.get("end_date", None) - if end_date_str: - job.finished = datetime.fromisoformat(end_date_str) - - return crud.update_job(self.db, job.job_id, job.model_dump(by_alias=True)) + except Exception as e: + raise HTTPException( + status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index f769bb0..88ad698 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -38,42 +38,60 @@ def __init__( ) def get_process_description(self, processId: str) -> Process: - process = crud.get_process(self.db, processId) - - # Convert metadata, links, inputs, and outputs if they exist - metadata = ( - [Metadata.model_validate(m) for m in process.metadata] - if process.metadata - else None - ) - links = ( - [Link.model_validate(link) for link in process.links] - if process.links - else None - ) - inputs = ( - {k: InputDescription.model_validate(v) for k, v in process.inputs.items()} - if process.inputs - else None - ) - outputs = ( - {k: OutputDescription.model_validate(v) for k, v in process.outputs.items()} - if process.outputs - else None - ) + lock_key = f"process:{processId}" + try: + with self.redis_locking_client.lock(lock_key, expire=60): + process = crud.get_process(self.db, processId) + + # Convert metadata, links, inputs, and outputs if they exist + metadata = ( + [Metadata.model_validate(m) for m in process.metadata] + if process.metadata + else None + ) + links = ( + [Link.model_validate(link) for link in process.links] + if process.links + else None + ) + inputs = ( + { + k: InputDescription.model_validate(v) + for k, v in process.inputs.items() + } + if process.inputs + else None + ) + outputs = ( + { + k: OutputDescription.model_validate(v) + for k, v in process.outputs.items() + } + if process.outputs + else None + ) - return Process( - title=process.title, - description=process.description, - keywords=process.keywords, - metadata=metadata, - id=process.id, - version=process.version, - job_control_options=process.job_control_options, - links=links, - inputs=inputs, - outputs=outputs, - ) + return Process( + title=process.title, + description=process.description, + keywords=process.keywords, + metadata=metadata, + id=process.id, + version=process.version, + job_control_options=process.job_control_options, + links=links, + inputs=inputs, + outputs=outputs, + ) + except self.redis_locking_client.LockError: + raise HTTPException( + status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + detail="Unable to acquire lock. Please try again later.", + ) + except Exception as e: + raise HTTPException( + status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) def get_processes(self) -> ProcessList: processes = crud.get_processes(self.db) @@ -97,77 +115,88 @@ def execute( response: str, prefer: str, ) -> Execute200Response: - check_process_integrity(self.db, processId, new_process=False) - - # Fetch process description - # process_description = self.get_process_description(processId) - # Validate inputs against schema - # validated_inputs = {} - # for input_id, input_value in execute_workflows.inputs.items(): - # input_description = next( - # (input for input in process_description.inputs if input.id == input_id), - # None, - # ) - # if input_description is None: - # raise HTTPException( - # status_code=fastapi_status.HTTP_400_BAD_REQUEST, - # detail=f"Invalid input: {input_id}", - # ) - # try: - # #validate(instance=input_value.value, schema=input_description.schema_) - # validated_inputs[input_id] = input_value.value - # except ValidationError as e: - # raise HTTPException( - # status_code=fastapi_status.HTTP_400_BAD_REQUEST, - # detail=f"Invalid input for {input_id}: {e.message}", - # ) - # validated_inputs[input_id] = input_value.value - - job_id = str(uuid.uuid4()) - logical_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") - - inputs_dict = {} - if execute_workflows.inputs: - for key, value in execute_workflows.inputs.items(): - try: - inputs_dict[key] = value.model_dump(exclude_unset=True) - except Exception: - inputs_dict[key] = value - - data = { - "dag_run_id": job_id, - "logical_date": logical_date, - "conf": inputs_dict, - } - + lock_key = f"process:{processId}" try: - airflow_response = requests.post( - f"{self.settings.EMS_API_URL}/dags/{processId}/dagRuns", - json=data, - auth=self.ems_api_auth, - ) - airflow_response.raise_for_status() - - job = StatusInfo( - processID=processId, - type="process", - jobID=job_id, - status=StatusCode.ACCEPTED, - created=datetime.now(), - updated=datetime.now(), - ) - crud.create_job(self.db, job.model_dump(by_alias=True)) - if prefer == "respond-async": - # Asynchronous execution - return job - else: - # Synchronous execution - # Note: In a real-world scenario, you'd wait for the job to complete - # and return the actual results. This is a simplified version. - return Execute200Response( - {"result": "Sample output for synchronous execution"} + with self.redis_locking_client.lock(lock_key, expire=60): + check_process_integrity(self.db, processId, new_process=False) + + # TODO + # Fetch process description + # Validate each execution input's value against the input schema in the process description + # If there is a way to fetch the Airflow DAG inputs, also validate against that. + # process_description = self.get_process_description(processId) + + # validated_inputs = {} + # for input_id, input_value in execute_workflows.inputs.items(): + # input_description = next( + # (input for input in process_description.inputs if input.id == input_id), + # None, + # ) + # if input_description is None: + # raise HTTPException( + # status_code=fastapi_status.HTTP_400_BAD_REQUEST, + # detail=f"Invalid input: {input_id}", + # ) + # try: + # #validate(instance=input_value.value, schema=input_description.schema_) + # validated_inputs[input_id] = input_value.value + # except ValidationError as e: + # raise HTTPException( + # status_code=fastapi_status.HTTP_400_BAD_REQUEST, + # detail=f"Invalid input for {input_id}: {e.message}", + # ) + # validated_inputs[input_id] = input_value.value + + job_id = str(uuid.uuid4()) + logical_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") + + inputs_dict = {} + if execute_workflows.inputs: + for key, value in execute_workflows.inputs.items(): + try: + inputs_dict[key] = value.model_dump(exclude_unset=True) + except Exception: + inputs_dict[key] = value + + data = { + "dag_run_id": job_id, + "logical_date": logical_date, + "conf": inputs_dict, + } + + airflow_response = requests.post( + f"{self.settings.EMS_API_URL}/dags/{processId}/dagRuns", + json=data, + auth=self.ems_api_auth, ) - + airflow_response.raise_for_status() + + job = StatusInfo( + processID=processId, + type="process", + jobID=job_id, + status=StatusCode.ACCEPTED, + created=datetime.now(), + updated=datetime.now(), + ) + crud.create_job(self.db, job.model_dump(by_alias=True)) + if prefer == "respond-async": + # Asynchronous execution + return job + else: + # Synchronous execution + # Note: In a real-world scenario, you'd wait for the job to complete + # and return the actual results. This is a simplified version. + # TODO get result from job using the result endpoint and return it here. + return Execute200Response( + {"result": "Sample output for synchronous execution"} + ) + + except self.redis_locking_client.LockError: + raise HTTPException( + status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + detail="Unable to acquire lock. Please try again later.", + ) except requests.exceptions.RequestException as e: status_code_to_raise = fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR detail_message = ( @@ -178,3 +207,7 @@ def execute( detail_message = f"Failed to start DAG run {job_id} with DAG {processId}: {e.response.status_code} {e.response.reason}" raise HTTPException(status_code=status_code_to_raise, detail=detail_message) + except Exception as e: + raise HTTPException( + status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) From 99d2107c7943b02e7df071486db033bf2dd76da1 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 5 Sep 2024 15:43:34 -0400 Subject: [PATCH 43/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/dru_api.py | 13 +++++++------ app-cp/src/openapi_server/impl/jobs_api.py | 19 ++++++++++--------- .../src/openapi_server/impl/processes_api.py | 9 +++++---- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/app-cp/src/openapi_server/impl/dru_api.py b/app-cp/src/openapi_server/impl/dru_api.py index 076538e..2c3ebf8 100644 --- a/app-cp/src/openapi_server/impl/dru_api.py +++ b/app-cp/src/openapi_server/impl/dru_api.py @@ -6,6 +6,7 @@ from fastapi import HTTPException, Response from fastapi import status from fastapi import status as fastapi_status +from redis.exceptions import LockError from requests.auth import HTTPBasicAuth from sqlalchemy.orm import Session from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound @@ -53,7 +54,7 @@ def __init__( def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: lock_key = f"process:{ogcapppkg.process_description.id}" try: - with self.redis_locking_client.lock(lock_key, expire=60): + with self.redis_locking_client.lock(lock_key, timeout=60): check_process_integrity( self.db, ogcapppkg.process_description.id, new_process=True ) @@ -131,7 +132,7 @@ def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: status_code=status.HTTP_201_CREATED, content=f"Process {ogcapppkg.process_description.id} deployed successfully", ) - except self.redis_locking_client.LockError: + except LockError: raise HTTPException( status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", @@ -144,7 +145,7 @@ def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: lock_key = f"process:{processId}" try: - with self.redis_locking_client.lock(lock_key): + with self.redis_locking_client.lock(lock_key, timeout=60): check_process_integrity(self.db, processId, new_process=False) # Validate the new ogcapppkg if ogcapppkg.process_description.id != processId: @@ -191,7 +192,7 @@ def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: status_code=status.HTTP_204_NO_CONTENT, content=f"Process {processId} replaced successfully", ) - except self.redis_locking_client.LockError: + except LockError: raise HTTPException( status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", @@ -204,7 +205,7 @@ def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: def undeploy(self, processId: str) -> None: lock_key = f"process:{processId}" try: - with self.redis_locking_client.lock(lock_key): + with self.redis_locking_client.lock(lock_key, timeout=60): check_process_integrity(self.db, processId, new_process=False) # Remove the DAG file from the deployed directory dag_filename = f"{processId}.py" @@ -232,7 +233,7 @@ def undeploy(self, processId: str) -> None: status_code=status.HTTP_204_NO_CONTENT, content=f"Process {processId} undeployed successfully", ) - except self.redis_locking_client.LockError: + except LockError: raise HTTPException( status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", diff --git a/app-cp/src/openapi_server/impl/jobs_api.py b/app-cp/src/openapi_server/impl/jobs_api.py index e258a07..b70c7be 100644 --- a/app-cp/src/openapi_server/impl/jobs_api.py +++ b/app-cp/src/openapi_server/impl/jobs_api.py @@ -4,6 +4,7 @@ import requests from fastapi import HTTPException from fastapi import status as fastapi_status +from redis.exceptions import LockError # from jsonschema import ValidationError, validate from requests.auth import HTTPBasicAuth @@ -58,10 +59,10 @@ def check_job_integrity(self, job_id: str, new_job: bool): def dismiss(self, jobId: str) -> StatusInfo: job_lock_key = f"job:{jobId}" try: - with self.redis_locking_client.lock(job_lock_key, expire=60): + with self.redis_locking_client.lock(job_lock_key, timeout=60): job = self.check_job_integrity(jobId, new_job=False) process_lock_key = f"process:{job.processID}" - with self.redis_locking_client.lock(process_lock_key, expire=60): + with self.redis_locking_client.lock(process_lock_key, timeout=60): response = requests.delete( f"{self.settings.EMS_API_URL}/dags/{job.processID}/dagRuns/{job.jobID}", auth=self.ems_api_auth, @@ -83,7 +84,7 @@ def dismiss(self, jobId: str) -> StatusInfo: links=job.links, ) - except self.redis_locking_client.LockError: + except LockError: raise HTTPException( status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", @@ -124,17 +125,17 @@ def get_jobs(self) -> JobList: def get_result(self, jobId: str, prefer: str) -> Dict[str, InlineOrRefData]: job_lock_key = f"job:{jobId}" try: - with self.redis_locking_client.lock(job_lock_key, expire=60): + with self.redis_locking_client.lock(job_lock_key, timeout=60): job = self.check_job_integrity(jobId, new_job=False) process_lock_key = f"process:{job.processID}" - with self.redis_locking_client.lock(process_lock_key, expire=60): + with self.redis_locking_client.lock(process_lock_key, timeout=60): results = crud.get_results(self.db, jobId) return { result.name: InlineOrRefData(href=result.href) for result in results } - except self.redis_locking_client.LockError: + except LockError: raise HTTPException( status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", @@ -147,10 +148,10 @@ def get_result(self, jobId: str, prefer: str) -> Dict[str, InlineOrRefData]: def get_status(self, jobId: str) -> StatusInfo: job_lock_key = f"job:{jobId}" try: - with self.redis_locking_client.lock(job_lock_key, expire=60): + with self.redis_locking_client.lock(job_lock_key, timeout=60): job = self.check_job_integrity(jobId, new_job=False) process_lock_key = f"process:{job.processID}" - with self.redis_locking_client.lock(process_lock_key, expire=60): + with self.redis_locking_client.lock(process_lock_key, timeout=60): job = StatusInfo( process_id=job.processID, type=job.type, @@ -193,7 +194,7 @@ def get_status(self, jobId: str) -> StatusInfo: self.db, job.job_id, job.model_dump(by_alias=True) ) - except self.redis_locking_client.LockError: + except LockError: raise HTTPException( status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index 88ad698..8e49cd4 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -4,6 +4,7 @@ import requests from fastapi import HTTPException from fastapi import status as fastapi_status +from redis.exceptions import LockError from requests.auth import HTTPBasicAuth from sqlalchemy.orm import Session @@ -40,7 +41,7 @@ def __init__( def get_process_description(self, processId: str) -> Process: lock_key = f"process:{processId}" try: - with self.redis_locking_client.lock(lock_key, expire=60): + with self.redis_locking_client.lock(lock_key, timeout=60): process = crud.get_process(self.db, processId) # Convert metadata, links, inputs, and outputs if they exist @@ -83,7 +84,7 @@ def get_process_description(self, processId: str) -> Process: inputs=inputs, outputs=outputs, ) - except self.redis_locking_client.LockError: + except LockError: raise HTTPException( status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", @@ -117,7 +118,7 @@ def execute( ) -> Execute200Response: lock_key = f"process:{processId}" try: - with self.redis_locking_client.lock(lock_key, expire=60): + with self.redis_locking_client.lock(lock_key, timeout=60): check_process_integrity(self.db, processId, new_process=False) # TODO @@ -192,7 +193,7 @@ def execute( {"result": "Sample output for synchronous execution"} ) - except self.redis_locking_client.LockError: + except LockError: raise HTTPException( status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", From 33c980f9b251322ee477f5171bf606177e94abc6 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 5 Sep 2024 16:10:52 -0400 Subject: [PATCH 44/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/dru_api.py | 128 +++++++++++++++--- app-cp/src/openapi_server/impl/jobs_api.py | 25 ++-- .../src/openapi_server/impl/processes_api.py | 17 ++- 3 files changed, 126 insertions(+), 44 deletions(-) diff --git a/app-cp/src/openapi_server/impl/dru_api.py b/app-cp/src/openapi_server/impl/dru_api.py index 2c3ebf8..39a2c55 100644 --- a/app-cp/src/openapi_server/impl/dru_api.py +++ b/app-cp/src/openapi_server/impl/dru_api.py @@ -3,9 +3,7 @@ import time import requests -from fastapi import HTTPException, Response -from fastapi import status -from fastapi import status as fastapi_status +from fastapi import HTTPException, Response, status from redis.exceptions import LockError from requests.auth import HTTPBasicAuth from sqlalchemy.orm import Session @@ -134,12 +132,15 @@ def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: ) except LockError: raise HTTPException( - status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", ) + except HTTPException: + # Re-raise HTTPExceptions without wrapping them + raise except Exception as e: raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) ) def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: @@ -194,12 +195,15 @@ def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: ) except LockError: raise HTTPException( - status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", ) + except HTTPException: + # Re-raise HTTPExceptions without wrapping them + raise except Exception as e: raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) ) def undeploy(self, processId: str) -> None: @@ -207,38 +211,118 @@ def undeploy(self, processId: str) -> None: try: with self.redis_locking_client.lock(lock_key, timeout=60): check_process_integrity(self.db, processId, new_process=False) + + ems_api_auth = HTTPBasicAuth( + self.settings.EMS_API_AUTH_USERNAME, + self.settings.EMS_API_AUTH_PASSWORD.get_secret_value(), + ) + + # Pause the DAG first + self.pause_dag( + self.settings.EMS_API_URL, processId, ems_api_auth, pause=True + ) + + # List and stop active DAG runs + active_dag_runs = self.list_active_dag_runs( + self.settings.EMS_API_URL, processId, ems_api_auth + ) + for dag_run in active_dag_runs: + self.stop_dag_run( + self.settings.EMS_API_URL, + processId, + dag_run["dag_run_id"], + ems_api_auth, + ) + self.stop_task_instances( + self.settings.EMS_API_URL, + processId, + dag_run["dag_run_id"], + ems_api_auth, + ) + # Remove the DAG file from the deployed directory dag_filename = f"{processId}.py" deployed_dag_path = os.path.join( self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename ) - if os.path.exists(deployed_dag_path): - os.remove(deployed_dag_path) + if os.path.isfile(deployed_dag_path): + try: + os.remove(deployed_dag_path) + except OSError as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to remove DAG file from deployed DAGs directory: {e.strerror}", + ) + + # Poll for the removal of the DAG from the Airflow API + timeout = 20 + start_time = time.time() + while time.time() - start_time < timeout: + response = requests.get( + f"{self.settings.EMS_API_URL}/dags/{processId}", + auth=ems_api_auth, + ) + data = response.json() + if response.status_code == 404: + break + elif not data["is_active"]: + break + time.sleep(0.5) + else: + raise HTTPException( + status_code=status.HTTP_504_GATEWAY_TIMEOUT, + detail="Timeout waiting for DAG to be fully removed from Airflow.", + ) # Delete the process from the database crud.delete_process(self.db, processId) - # Optionally, you might want to pause or delete the DAG in Airflow - ems_api_auth = HTTPBasicAuth( - self.settings.EMS_API_AUTH_USERNAME, - self.settings.EMS_API_AUTH_PASSWORD.get_secret_value(), - ) - response = requests.delete( - f"{self.settings.EMS_API_URL}/dags/{processId}", - auth=ems_api_auth, - ) - response.raise_for_status() - return Response( status_code=status.HTTP_204_NO_CONTENT, content=f"Process {processId} undeployed successfully", ) except LockError: raise HTTPException( - status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", ) + except HTTPException: + # Re-raise HTTPExceptions without wrapping them + raise except Exception as e: + # For any other exception, wrap it in a generic HTTPException raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) + + def pause_dag(self, airflow_url, dag_id, auth, pause=True): + endpoint = f"{airflow_url}/dags/{dag_id}" + data = {"is_paused": pause} + response = requests.patch(endpoint, auth=auth, json=data) + response.raise_for_status() + + def list_active_dag_runs(self, airflow_url, dag_id, auth): + endpoint = f"{airflow_url}/dags/{dag_id}/dagRuns" + params = {"state": "running"} + response = requests.get(endpoint, auth=auth, params=params) + response.raise_for_status() + return response.json()["dag_runs"] + + def stop_dag_run(self, airflow_url, dag_id, dag_run_id, auth): + endpoint = f"{airflow_url}/dags/{dag_id}/dagRuns/{dag_run_id}" + data = {"state": "failed"} + response = requests.patch(endpoint, auth=auth, json=data) + response.raise_for_status() + + def stop_task_instances(self, airflow_url, dag_id, dag_run_id, auth): + endpoint = f"{airflow_url}/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances" + tasks = requests.get(endpoint, auth=auth) + tasks.raise_for_status() + + for task in tasks.json()["task_instances"]: + task_instance_endpoint = f"{airflow_url}/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task['task_id']}" + update_data = {"dry_run": False, "new_state": "failed"} + update_response = requests.patch( + task_instance_endpoint, auth=auth, json=update_data ) + update_response.raise_for_status() diff --git a/app-cp/src/openapi_server/impl/jobs_api.py b/app-cp/src/openapi_server/impl/jobs_api.py index b70c7be..b9d7359 100644 --- a/app-cp/src/openapi_server/impl/jobs_api.py +++ b/app-cp/src/openapi_server/impl/jobs_api.py @@ -2,8 +2,7 @@ from typing import Dict import requests -from fastapi import HTTPException -from fastapi import status as fastapi_status +from fastapi import HTTPException, status from redis.exceptions import LockError # from jsonschema import ValidationError, validate @@ -41,17 +40,17 @@ def check_job_integrity(self, job_id: str, new_job: bool): except NoResultFound: if not new_job: raise HTTPException( - status_code=fastapi_status.HTTP_404_NOT_FOUND, + status_code=status.HTTP_404_NOT_FOUND, detail=f"Job with ID '{job_id}' not found", ) except MultipleResultsFound: raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Multiple jobs found with same ID '{job_id}', data integrity error", ) except ValueError: raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Existing job with ID '{job_id}' already exists", ) return job @@ -86,17 +85,17 @@ def dismiss(self, jobId: str) -> StatusInfo: except LockError: raise HTTPException( - status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", ) except requests.exceptions.HTTPError as e: raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to delete DAG run {job.jobID} for DAG {job.processID}: {e}", ) except Exception as e: raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) ) def get_jobs(self) -> JobList: @@ -137,12 +136,12 @@ def get_result(self, jobId: str, prefer: str) -> Dict[str, InlineOrRefData]: except LockError: raise HTTPException( - status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", ) except Exception as e: raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) ) def get_status(self, jobId: str) -> StatusInfo: @@ -196,15 +195,15 @@ def get_status(self, jobId: str) -> StatusInfo: except LockError: raise HTTPException( - status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", ) except requests.exceptions.HTTPError as e: raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to fetch DAG run {job.job_id} for DAG {job.process_id}: {e}", ) except Exception as e: raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) ) diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index 8e49cd4..196005c 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -2,8 +2,7 @@ from datetime import datetime import requests -from fastapi import HTTPException -from fastapi import status as fastapi_status +from fastapi import HTTPException, status from redis.exceptions import LockError from requests.auth import HTTPBasicAuth from sqlalchemy.orm import Session @@ -86,12 +85,12 @@ def get_process_description(self, processId: str) -> Process: ) except LockError: raise HTTPException( - status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", ) except Exception as e: raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) ) def get_processes(self) -> ProcessList: @@ -135,7 +134,7 @@ def execute( # ) # if input_description is None: # raise HTTPException( - # status_code=fastapi_status.HTTP_400_BAD_REQUEST, + # status_code=status.HTTP_400_BAD_REQUEST, # detail=f"Invalid input: {input_id}", # ) # try: @@ -143,7 +142,7 @@ def execute( # validated_inputs[input_id] = input_value.value # except ValidationError as e: # raise HTTPException( - # status_code=fastapi_status.HTTP_400_BAD_REQUEST, + # status_code=status.HTTP_400_BAD_REQUEST, # detail=f"Invalid input for {input_id}: {e.message}", # ) # validated_inputs[input_id] = input_value.value @@ -195,11 +194,11 @@ def execute( except LockError: raise HTTPException( - status_code=fastapi_status.HTTP_503_SERVICE_UNAVAILABLE, + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unable to acquire lock. Please try again later.", ) except requests.exceptions.RequestException as e: - status_code_to_raise = fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR + status_code_to_raise = status.HTTP_500_INTERNAL_SERVER_ERROR detail_message = ( f"Failed to start DAG run {job_id} with DAG {processId}: {str(e)}" ) @@ -210,5 +209,5 @@ def execute( raise HTTPException(status_code=status_code_to_raise, detail=detail_message) except Exception as e: raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) ) From afad60f412f1360cc2951f679fcf5cbcf9f00b22 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 5 Sep 2024 16:25:52 -0400 Subject: [PATCH 45/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/dru_api.py | 6 +++--- app-cp/src/openapi_server/impl/jobs_api.py | 12 ++++++------ app-cp/src/openapi_server/impl/processes_api.py | 4 ++-- app-cp/src/openapi_server/utils/redis.py | 10 ++++++---- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/app-cp/src/openapi_server/impl/dru_api.py b/app-cp/src/openapi_server/impl/dru_api.py index 39a2c55..25d1332 100644 --- a/app-cp/src/openapi_server/impl/dru_api.py +++ b/app-cp/src/openapi_server/impl/dru_api.py @@ -52,7 +52,7 @@ def __init__( def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: lock_key = f"process:{ogcapppkg.process_description.id}" try: - with self.redis_locking_client.lock(lock_key, timeout=60): + with self.redis_locking_client.lock(lock_key, lock_timeout=60): check_process_integrity( self.db, ogcapppkg.process_description.id, new_process=True ) @@ -146,7 +146,7 @@ def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: lock_key = f"process:{processId}" try: - with self.redis_locking_client.lock(lock_key, timeout=60): + with self.redis_locking_client.lock(lock_key, lock_timeout=60): check_process_integrity(self.db, processId, new_process=False) # Validate the new ogcapppkg if ogcapppkg.process_description.id != processId: @@ -209,7 +209,7 @@ def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: def undeploy(self, processId: str) -> None: lock_key = f"process:{processId}" try: - with self.redis_locking_client.lock(lock_key, timeout=60): + with self.redis_locking_client.lock(lock_key, lock_timeout=60): check_process_integrity(self.db, processId, new_process=False) ems_api_auth = HTTPBasicAuth( diff --git a/app-cp/src/openapi_server/impl/jobs_api.py b/app-cp/src/openapi_server/impl/jobs_api.py index b9d7359..a16ad0c 100644 --- a/app-cp/src/openapi_server/impl/jobs_api.py +++ b/app-cp/src/openapi_server/impl/jobs_api.py @@ -58,10 +58,10 @@ def check_job_integrity(self, job_id: str, new_job: bool): def dismiss(self, jobId: str) -> StatusInfo: job_lock_key = f"job:{jobId}" try: - with self.redis_locking_client.lock(job_lock_key, timeout=60): + with self.redis_locking_client.lock(job_lock_key): job = self.check_job_integrity(jobId, new_job=False) process_lock_key = f"process:{job.processID}" - with self.redis_locking_client.lock(process_lock_key, timeout=60): + with self.redis_locking_client.lock(process_lock_key): response = requests.delete( f"{self.settings.EMS_API_URL}/dags/{job.processID}/dagRuns/{job.jobID}", auth=self.ems_api_auth, @@ -124,10 +124,10 @@ def get_jobs(self) -> JobList: def get_result(self, jobId: str, prefer: str) -> Dict[str, InlineOrRefData]: job_lock_key = f"job:{jobId}" try: - with self.redis_locking_client.lock(job_lock_key, timeout=60): + with self.redis_locking_client.lock(job_lock_key): job = self.check_job_integrity(jobId, new_job=False) process_lock_key = f"process:{job.processID}" - with self.redis_locking_client.lock(process_lock_key, timeout=60): + with self.redis_locking_client.lock(process_lock_key): results = crud.get_results(self.db, jobId) return { result.name: InlineOrRefData(href=result.href) @@ -147,10 +147,10 @@ def get_result(self, jobId: str, prefer: str) -> Dict[str, InlineOrRefData]: def get_status(self, jobId: str) -> StatusInfo: job_lock_key = f"job:{jobId}" try: - with self.redis_locking_client.lock(job_lock_key, timeout=60): + with self.redis_locking_client.lock(job_lock_key): job = self.check_job_integrity(jobId, new_job=False) process_lock_key = f"process:{job.processID}" - with self.redis_locking_client.lock(process_lock_key, timeout=60): + with self.redis_locking_client.lock(process_lock_key): job = StatusInfo( process_id=job.processID, type=job.type, diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app-cp/src/openapi_server/impl/processes_api.py index 196005c..0c5b37b 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app-cp/src/openapi_server/impl/processes_api.py @@ -40,7 +40,7 @@ def __init__( def get_process_description(self, processId: str) -> Process: lock_key = f"process:{processId}" try: - with self.redis_locking_client.lock(lock_key, timeout=60): + with self.redis_locking_client.lock(lock_key): process = crud.get_process(self.db, processId) # Convert metadata, links, inputs, and outputs if they exist @@ -117,7 +117,7 @@ def execute( ) -> Execute200Response: lock_key = f"process:{processId}" try: - with self.redis_locking_client.lock(lock_key, timeout=60): + with self.redis_locking_client.lock(lock_key): check_process_integrity(self.db, processId, new_process=False) # TODO diff --git a/app-cp/src/openapi_server/utils/redis.py b/app-cp/src/openapi_server/utils/redis.py index 49851b3..6e9cdc3 100644 --- a/app-cp/src/openapi_server/utils/redis.py +++ b/app-cp/src/openapi_server/utils/redis.py @@ -11,10 +11,12 @@ def __init__(self, client=None, host="localhost", port=6379, db=0): self.client = client @contextmanager - def lock(self, lock_id, timeout=10): - """Attempt to acquire a lock within the given timeout period.""" - lock = self.client.lock(lock_id, timeout=timeout) - acquired = lock.acquire(blocking=True, blocking_timeout=timeout) + def lock(self, lock_id: str, lock_timeout: int = 10, blocking_timeout: int = 1): + # Create a Redis lock object with the specified expiration timeout + lock = self.client.lock(lock_id, timeout=lock_timeout) + + # Try to acquire the lock, waiting up to blocking_timeout seconds + acquired = lock.acquire(blocking=True, blocking_timeout=blocking_timeout) try: if acquired: yield lock From 93c2cbef10f736ac88b8c53d8b08a6903e5c50af Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 5 Sep 2024 16:35:30 -0400 Subject: [PATCH 46/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/database/models.py | 38 +++++++++++++++----- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/app-cp/src/openapi_server/database/models.py b/app-cp/src/openapi_server/database/models.py index f6b8e36..b3d7e0b 100644 --- a/app-cp/src/openapi_server/database/models.py +++ b/app-cp/src/openapi_server/database/models.py @@ -18,7 +18,18 @@ class Process(Base): inputs = Column(JSON) outputs = Column(JSON) deployment_status = Column(String, default="pending") - jobs = relationship("Job", back_populates="process") + jobs = relationship( + "Job", + back_populates="process", + cascade="all, delete-orphan", + passive_deletes=True, + ) + ogcapppkg = relationship( + "Ogcapppkg", + back_populates="process", + cascade="all, delete-orphan", + passive_deletes=True, + ) # https://docs.sqlalchemy.org/en/20/orm/declarative_tables.html#appending-additional-columns-to-an-existing-declarative-mapped-class @@ -33,22 +44,28 @@ class ExecutionUnit(Base): deployment = Column(String) config = Column(JSON) additional_properties = Column(JSON) - ogcapppkg_id = Column(Integer, ForeignKey("ogcapppkgs._id")) + ogcapppkg_id = Column(Integer, ForeignKey("ogcapppkgs._id", ondelete="CASCADE")) class Ogcapppkg(Base): __tablename__ = "ogcapppkgs" _id = Column(Integer, primary_key=True) - process_id = Column(String, ForeignKey("processes.id")) - process = relationship("Process", backref="ogcapppkg") - execution_unit = relationship("ExecutionUnit", uselist=False, backref="ogcapppkg") + process_id = Column(String, ForeignKey("processes.id", ondelete="CASCADE")) + process = relationship("Process", back_populates="ogcapppkg") + execution_unit = relationship( + "ExecutionUnit", + uselist=False, + back_populates="ogcapppkg", + cascade="all, delete-orphan", + passive_deletes=True, + ) class Job(Base): __tablename__ = "jobs" _id = Column(Integer, primary_key=True) jobID = Column(String, index=True, unique=True, nullable=False) - processID = Column(String, ForeignKey("processes.id")) + processID = Column(String, ForeignKey("processes.id", ondelete="CASCADE")) process = relationship("Process", back_populates="jobs") type = Column(String) status = Column(String) @@ -63,12 +80,17 @@ class Job(Base): inputs = Column(JSON) outputs = Column(JSON) subscriber = Column(JSON) - results = relationship("Result", back_populates="job") + results = relationship( + "Result", + back_populates="job", + cascade="all, delete-orphan", + passive_deletes=True, + ) class Result(Base): __tablename__ = "results" _id = Column(Integer, primary_key=True) - jobID = Column(String, ForeignKey("jobs.jobID")) + jobID = Column(String, ForeignKey("jobs.jobID", ondelete="CASCADE")) job = relationship("Job", back_populates="results") root = Column(JSON) From 373ab65b5ac5eea641ff3636d740d226e1a3f31a Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 5 Sep 2024 16:41:43 -0400 Subject: [PATCH 47/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/database/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app-cp/src/openapi_server/database/models.py b/app-cp/src/openapi_server/database/models.py index b3d7e0b..54dfa43 100644 --- a/app-cp/src/openapi_server/database/models.py +++ b/app-cp/src/openapi_server/database/models.py @@ -45,6 +45,7 @@ class ExecutionUnit(Base): config = Column(JSON) additional_properties = Column(JSON) ogcapppkg_id = Column(Integer, ForeignKey("ogcapppkgs._id", ondelete="CASCADE")) + ogcapppkg = relationship("Ogcapppkg", back_populates="execution_unit") class Ogcapppkg(Base): From 22d6cfce73aa97a83d96b31d9686c4e9ca527239 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 5 Sep 2024 16:47:17 -0400 Subject: [PATCH 48/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/database/models.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app-cp/src/openapi_server/database/models.py b/app-cp/src/openapi_server/database/models.py index 54dfa43..c8f06d5 100644 --- a/app-cp/src/openapi_server/database/models.py +++ b/app-cp/src/openapi_server/database/models.py @@ -51,8 +51,10 @@ class ExecutionUnit(Base): class Ogcapppkg(Base): __tablename__ = "ogcapppkgs" _id = Column(Integer, primary_key=True) - process_id = Column(String, ForeignKey("processes.id", ondelete="CASCADE")) - process = relationship("Process", back_populates="ogcapppkg") + process_id = Column( + String, ForeignKey("processes.id", ondelete="CASCADE"), nullable=False + ) + process = relationship("Process", back_populates="ogcapppkg", passive_deletes=True) execution_unit = relationship( "ExecutionUnit", uselist=False, From c67a072bc8f975b6f0b2d91b5ad43d7b53315ce2 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 5 Sep 2024 17:29:19 -0400 Subject: [PATCH 49/60] feat: Initial autogenerated FastAPI implementation --- app-cp/src/openapi_server/impl/dru_api.py | 17 ++++++++++++++--- .../unity_sps_ogc_processes_api/apis/dru_api.py | 11 +++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/app-cp/src/openapi_server/impl/dru_api.py b/app-cp/src/openapi_server/impl/dru_api.py index 25d1332..e1b94d6 100644 --- a/app-cp/src/openapi_server/impl/dru_api.py +++ b/app-cp/src/openapi_server/impl/dru_api.py @@ -206,7 +206,7 @@ def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) ) - def undeploy(self, processId: str) -> None: + def undeploy(self, processId: str, force: bool = False) -> None: lock_key = f"process:{processId}" try: with self.redis_locking_client.lock(lock_key, lock_timeout=60): @@ -217,15 +217,26 @@ def undeploy(self, processId: str) -> None: self.settings.EMS_API_AUTH_PASSWORD.get_secret_value(), ) - # Pause the DAG first + # Check for active DAG runs + active_dag_runs = self.list_active_dag_runs( + self.settings.EMS_API_URL, processId, ems_api_auth + ) + if active_dag_runs and not force: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail="Process has active DAG runs. Set 'force' to true to override and stop all active DAG runs and tasks.", + ) + + # Pause the DAG self.pause_dag( self.settings.EMS_API_URL, processId, ems_api_auth, pause=True ) - # List and stop active DAG runs + # Get active DAG runs again after the DAG is paused active_dag_runs = self.list_active_dag_runs( self.settings.EMS_API_URL, processId, ems_api_auth ) + for dag_run in active_dag_runs: self.stop_dag_run( self.settings.EMS_API_URL, diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py b/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py index f97f0c4..de701fa 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py +++ b/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py @@ -100,6 +100,10 @@ async def replace( "model": Exception, "description": "The requested resource does not exist on the server. For example, a path parameter had an incorrect value.", }, + 409: { + "model": Exception, + "description": "The process has active DAG runs and force is not set to true.", + }, 500: {"model": Exception, "description": "A server error occurred."}, }, tags=["DRU"], @@ -111,7 +115,10 @@ async def undeploy( redis_locking_client: RedisLock = Depends(get_redis_locking_client), db: Session = Depends(get_db), processId: str = Path(..., description=""), + force: bool = Query( + False, description="Force undeployment even if there are active DAG runs" + ), ) -> None: - """Undeploys a process. For more information, see [Section 6.5](http://docs.ogc.org/DRAFTS/20-044.html#_16391f9e-538f-4a84-9710-72a6bab82842).""" + """Undeploys a process. For more information, see [Section 6.5](http://docs.ogc.org/DRAFTS/20-044.html#_16391f9e-538f-4a84-9710-72a6bab82842).""" dru_api = BaseDRUApi.subclasses[0](settings, redis_locking_client, db) - return dru_api.undeploy(processId) + return dru_api.undeploy(processId, force) From 1fb37be95980e26531ed21a3b5a89daccb6a969d Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 5 Sep 2024 18:23:34 -0400 Subject: [PATCH 50/60] feat: Initial autogenerated FastAPI implementation --- Dockerfile | 2 +- .../openapi_server/config => }/__init__.py | 0 .../{src/openapi_server/config => }/config.py | 0 {app => app-cp}/database/__init__.py | 0 {app => app-cp}/database/crud.py | 0 {app => app-cp}/database/models.py | 0 {app => app-cp}/main.py | 0 {app => app-cp}/redis.py | 0 .../impl => schemas}/__init__.py | 0 {app => app-cp}/schemas/ogc_processes.py | 11 +- {app => app-cp}/schemas/unity_sps.py | 0 {app-cp => app}/.flake8 | 0 {app-cp => app}/.gitignore | 0 {app-cp => app}/.openapi-generator-ignore | 0 {app-cp => app}/.openapi-generator/FILES | 0 {app-cp => app}/.openapi-generator/VERSION | 0 {app-cp => app}/Dockerfile | 0 {app-cp => app}/README.md | 0 {app-cp => app}/docker-compose.yaml | 0 {app-cp => app}/openapi.yaml | 0 {app-cp => app}/pyproject.toml | 0 .../src/openapi_server/config}/__init__.py | 0 app/{ => src/openapi_server/config}/config.py | 0 .../src/openapi_server/database/__init__.py | 0 .../src/openapi_server/database/crud.py | 5 +- .../src/openapi_server/database/models.py | 4 +- .../src/openapi_server/impl}/__init__.py | 0 .../src/openapi_server/impl/api_api.py | 1 - .../openapi_server/impl/conformance_api.py | 0 .../src/openapi_server/impl/dru_api.py | 69 ++++-------- app/src/openapi_server/impl/health_api.py | 7 ++ .../src/openapi_server/impl/jobs_api.py | 36 ++---- .../openapi_server/impl/landing_page_api.py | 4 +- .../src/openapi_server/impl/processes_api.py | 47 ++------ .../src/openapi_server/utils}/__init__.py | 0 .../src/openapi_server/utils/redis.py | 0 .../unity_sps_ogc_processes_api}/__init__.py | 0 .../apis}/__init__.py | 0 .../apis/api_api.py | 3 +- .../apis/api_api_base.py | 0 .../apis/conformance_api.py | 3 +- .../apis/conformance_api_base.py | 0 .../apis/dru_api.py | 23 +--- .../apis/dru_api_base.py | 0 .../apis/health_api.py | 32 ++++++ .../apis/health_api_base.py | 22 ++++ .../apis/jobs_api.py | 11 +- .../apis/jobs_api_base.py | 0 .../apis/landing_page_api.py | 3 +- .../apis/landing_page_api_base.py | 0 .../apis/processes_api.py | 11 +- .../apis/processes_api_base.py | 0 .../dependencies.py | 0 .../src/unity_sps_ogc_processes_api/main.py | 17 +-- .../models}/__init__.py | 0 .../models/bbox.py | 7 +- .../models/bbox1.py | 7 +- .../models/bbox_def_crs.py | 16 +-- .../models/bbox_processes.py | 7 +- .../models/collection_info.py | 35 ++---- .../models/collection_info_data_type.py | 0 .../models/collections.py | 6 +- .../models/conf_classes.py | 0 .../unity_sps_ogc_processes_api/models/crs.py | 9 +- .../models/crs_one_of.py | 29 ++--- .../models/crs_one_of_one_of.py | 4 +- .../models/crs_one_of_one_of1.py | 0 .../models/crs_one_of_one_of2.py | 0 .../models/data_type.py | 8 +- .../models/description_type.py | 1 - .../models/enumeration.py | 0 .../models/exception.py | 0 .../models/execute.py | 15 +-- .../models/execute200_response.py | 5 +- .../models/execute200_response1.py | 14 +-- .../models/execute_workflows.py | 19 +--- .../models/execute_workflows1.py | 19 +--- .../models/execution_unit.py | 9 +- .../models/execution_unit_config.py | 0 .../models/extent.py | 9 +- .../models/extent_spatial.py | 27 +---- .../models/extent_spatial_grid_inner.py | 11 +- ...nt_spatial_grid_inner_coordinates_inner.py | 17 +-- .../extent_spatial_grid_inner_resolution.py | 17 +-- .../models/extent_temporal.py | 9 +- .../models/extent_temporal_grid.py | 13 +-- .../models/extent_temporal_grid_resolution.py | 17 +-- .../models/extent_uad.py | 9 +- .../models/extra_models.py | 0 .../models/feature_collection.py | 14 +-- .../models/fields_modifiers.py | 5 +- .../models/fields_modifiers_properties.py | 0 .../models/format.py | 1 - .../models/format_schema.py | 8 +- .../models/geo_json_feature.py | 24 +--- .../models/geo_json_feature_geometry.py | 41 ++----- .../models/geo_json_feature_id.py | 17 +-- .../models/geo_json_line_string.py | 13 +-- .../models/geo_json_multi_line_string.py | 13 +-- .../models/geo_json_multi_point.py | 17 +-- .../models/geo_json_multi_polygon.py | 17 +-- .../models/geo_json_point.py | 13 +-- .../models/geo_json_polygon.py | 13 +-- .../models/health_check.py | 5 + .../models/inline_or_ref_data.py | 5 +- .../models/inline_or_ref_data1.py | 33 ++---- .../models/inline_or_ref_data_workflows.py | 33 ++---- .../models/input.py | 13 +-- .../models/input_collection.py | 5 +- .../models/input_description.py | 1 - .../input_description_all_of_max_occurs.py | 8 +- .../models/input_parameterized.py | 5 +- .../models/input_process.py | 19 +--- .../models/input_value.py | 13 +-- .../models/input_value1.py | 17 +-- .../models/input_value_no_object.py | 13 +-- .../models/input_value_no_object1.py | 13 +-- .../models/input_value_no_object_workflows.py | 21 +--- .../models/input_value_workflows.py | 16 +-- .../models/input_workflows.py | 5 +- .../models/input_workflows1.py | 24 +--- .../models/job_control_options.py | 0 .../models/job_list.py | 1 - .../models/landing_page.py | 1 - .../models/link.py | 0 .../models/metadata.py | 1 - .../models/metadata_one_of.py | 0 .../models/metadata_one_of1.py | 9 +- .../models/metadata_one_of1_value.py | 8 +- .../models/model_schema.py | 1 - .../models/ogcapppkg.py | 5 +- .../models/ogcapppkg_array_inner.py | 17 +-- .../models/ogcapppkg_execution_unit.py | 1 - .../models/output.py | 9 +- .../models/output_description.py | 1 - .../models/output_workflows.py | 7 +- .../models/output_workflows1.py | 7 +- .../models/process.py | 15 +-- .../models/process_list.py | 1 - .../models/process_summary.py | 5 +- .../models/processes_list.py | 0 .../models/qualified_input_value.py | 1 - .../models/qualified_input_value1.py | 1 - .../models/qualified_input_value_workflows.py | 5 +- .../models/reference.py | 0 .../models/schema1.py | 9 +- .../models/schema_one_of.py | 105 ++++-------------- .../schema_one_of_additional_properties.py | 8 +- .../models/static_indicator.py | 9 +- .../models/status_code.py | 0 .../models/status_info.py | 5 +- .../models/subscriber.py | 0 .../security_api.py | 6 +- {app-cp => app}/tests/conftest.py | 33 ++---- app/tests/echoProcess.json | 73 ++++++++++++ {app-cp => app}/tests/test_api_api.py | 1 - {app-cp => app}/tests/test_conformance_api.py | 1 - {app-cp => app}/tests/test_dru_api.py | 3 +- {app-cp => app}/tests/test_jobs_api.py | 5 +- .../tests/test_landing_page_api.py | 1 - {app-cp => app}/tests/test_processes_api.py | 17 +-- 161 files changed, 436 insertions(+), 1031 deletions(-) rename app-cp/{src/openapi_server/config => }/__init__.py (100%) rename app-cp/{src/openapi_server/config => }/config.py (100%) rename {app => app-cp}/database/__init__.py (100%) rename {app => app-cp}/database/crud.py (100%) rename {app => app-cp}/database/models.py (100%) rename {app => app-cp}/main.py (100%) rename {app => app-cp}/redis.py (100%) rename app-cp/{src/openapi_server/impl => schemas}/__init__.py (100%) rename {app => app-cp}/schemas/ogc_processes.py (99%) rename {app => app-cp}/schemas/unity_sps.py (100%) rename {app-cp => app}/.flake8 (100%) rename {app-cp => app}/.gitignore (100%) rename {app-cp => app}/.openapi-generator-ignore (100%) rename {app-cp => app}/.openapi-generator/FILES (100%) rename {app-cp => app}/.openapi-generator/VERSION (100%) rename {app-cp => app}/Dockerfile (100%) rename {app-cp => app}/README.md (100%) rename {app-cp => app}/docker-compose.yaml (100%) rename {app-cp => app}/openapi.yaml (100%) rename {app-cp => app}/pyproject.toml (100%) rename {app-cp/src/openapi_server/utils => app/src/openapi_server/config}/__init__.py (100%) rename app/{ => src/openapi_server/config}/config.py (100%) rename {app-cp => app}/src/openapi_server/database/__init__.py (100%) rename {app-cp => app}/src/openapi_server/database/crud.py (95%) rename {app-cp => app}/src/openapi_server/database/models.py (96%) rename {app-cp/src/unity_sps_ogc_processes_api => app/src/openapi_server/impl}/__init__.py (100%) rename {app-cp => app}/src/openapi_server/impl/api_api.py (99%) rename {app-cp => app}/src/openapi_server/impl/conformance_api.py (100%) rename {app-cp => app}/src/openapi_server/impl/dru_api.py (85%) create mode 100644 app/src/openapi_server/impl/health_api.py rename {app-cp => app}/src/openapi_server/impl/jobs_api.py (89%) rename {app-cp => app}/src/openapi_server/impl/landing_page_api.py (92%) rename {app-cp => app}/src/openapi_server/impl/processes_api.py (86%) rename {app-cp/src/unity_sps_ogc_processes_api/apis => app/src/openapi_server/utils}/__init__.py (100%) rename {app-cp => app}/src/openapi_server/utils/redis.py (100%) rename {app-cp/src/unity_sps_ogc_processes_api/models => app/src/unity_sps_ogc_processes_api}/__init__.py (100%) rename app/{ => src/unity_sps_ogc_processes_api/apis}/__init__.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/apis/api_api.py (99%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/apis/api_api_base.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/apis/conformance_api.py (99%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/apis/dru_api.py (89%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/apis/dru_api_base.py (100%) create mode 100644 app/src/unity_sps_ogc_processes_api/apis/health_api.py create mode 100644 app/src/unity_sps_ogc_processes_api/apis/health_api_base.py rename {app-cp => app}/src/unity_sps_ogc_processes_api/apis/jobs_api.py (97%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/apis/landing_page_api.py (99%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/apis/landing_page_api_base.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/apis/processes_api.py (98%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/apis/processes_api_base.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/dependencies.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/main.py (82%) rename app/{schemas => src/unity_sps_ogc_processes_api/models}/__init__.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/bbox.py (93%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/bbox1.py (93%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py (89%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/bbox_processes.py (93%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/collection_info.py (86%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/collection_info_data_type.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/collections.py (96%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/conf_classes.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/crs.py (94%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/crs_one_of.py (87%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py (95%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of1.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of2.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/data_type.py (94%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/description_type.py (99%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/enumeration.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/exception.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/execute.py (88%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/execute200_response.py (93%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/execute200_response1.py (93%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/execute_workflows.py (90%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/execute_workflows1.py (90%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/execution_unit.py (94%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/execution_unit_config.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/extent.py (93%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/extent_spatial.py (89%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py (93%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py (92%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py (92%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/extent_temporal.py (95%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py (90%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py (92%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/extent_uad.py (93%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/extra_models.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/feature_collection.py (93%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/fields_modifiers.py (98%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/format.py (99%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/format_schema.py (94%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/geo_json_feature.py (89%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py (87%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py (92%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py (92%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py (92%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py (89%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py (89%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/geo_json_point.py (92%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py (92%) create mode 100644 app/src/unity_sps_ogc_processes_api/models/health_check.py rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py (94%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py (86%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py (86%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/input.py (87%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/input_collection.py (98%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/input_description.py (99%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py (94%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/input_parameterized.py (98%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/input_process.py (90%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/input_value.py (91%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/input_value1.py (91%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/input_value_no_object.py (95%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py (95%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py (94%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/input_value_workflows.py (91%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/input_workflows.py (98%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/input_workflows1.py (88%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/job_control_options.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/job_list.py (99%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/landing_page.py (99%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/link.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/metadata.py (99%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/metadata_one_of.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py (96%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py (94%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/model_schema.py (99%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/ogcapppkg.py (98%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py (91%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py (99%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/output.py (92%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/output_description.py (99%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/output_workflows.py (93%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/output_workflows1.py (93%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/process.py (93%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/process_list.py (99%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/process_summary.py (98%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/processes_list.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/qualified_input_value.py (99%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py (99%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py (98%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/reference.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/schema1.py (94%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/schema_one_of.py (77%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py (94%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/static_indicator.py (95%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/status_code.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/status_info.py (96%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/models/subscriber.py (100%) rename {app-cp => app}/src/unity_sps_ogc_processes_api/security_api.py (77%) rename {app-cp => app}/tests/conftest.py (96%) create mode 100644 app/tests/echoProcess.json rename {app-cp => app}/tests/test_api_api.py (99%) rename {app-cp => app}/tests/test_conformance_api.py (99%) rename {app-cp => app}/tests/test_dru_api.py (96%) rename {app-cp => app}/tests/test_jobs_api.py (97%) rename {app-cp => app}/tests/test_landing_page_api.py (99%) rename {app-cp => app}/tests/test_processes_api.py (89%) diff --git a/Dockerfile b/Dockerfile index 0e2054f..3acdd65 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ FROM python:3.12-slim RUN apt-get update && apt-get install -y git gcc libpq-dev # Copy the current directory contents into the container at /app -COPY ./app-cp /app +COPY ./app /app WORKDIR /app diff --git a/app-cp/src/openapi_server/config/__init__.py b/app-cp/__init__.py similarity index 100% rename from app-cp/src/openapi_server/config/__init__.py rename to app-cp/__init__.py diff --git a/app-cp/src/openapi_server/config/config.py b/app-cp/config.py similarity index 100% rename from app-cp/src/openapi_server/config/config.py rename to app-cp/config.py diff --git a/app/database/__init__.py b/app-cp/database/__init__.py similarity index 100% rename from app/database/__init__.py rename to app-cp/database/__init__.py diff --git a/app/database/crud.py b/app-cp/database/crud.py similarity index 100% rename from app/database/crud.py rename to app-cp/database/crud.py diff --git a/app/database/models.py b/app-cp/database/models.py similarity index 100% rename from app/database/models.py rename to app-cp/database/models.py diff --git a/app/main.py b/app-cp/main.py similarity index 100% rename from app/main.py rename to app-cp/main.py diff --git a/app/redis.py b/app-cp/redis.py similarity index 100% rename from app/redis.py rename to app-cp/redis.py diff --git a/app-cp/src/openapi_server/impl/__init__.py b/app-cp/schemas/__init__.py similarity index 100% rename from app-cp/src/openapi_server/impl/__init__.py rename to app-cp/schemas/__init__.py diff --git a/app/schemas/ogc_processes.py b/app-cp/schemas/ogc_processes.py similarity index 99% rename from app/schemas/ogc_processes.py rename to app-cp/schemas/ogc_processes.py index 169ee73..99b104c 100644 --- a/app/schemas/ogc_processes.py +++ b/app-cp/schemas/ogc_processes.py @@ -8,16 +8,7 @@ from enum import Enum from typing import Any, Dict, List, Optional, Set, Union -from pydantic import ( - AnyUrl, - BaseModel, - ConfigDict, - Field, - PositiveFloat, - RootModel, - confloat, - conint, -) +from pydantic import AnyUrl, BaseModel, ConfigDict, Field, PositiveFloat, RootModel, confloat, conint class BaseSchema(BaseModel): diff --git a/app/schemas/unity_sps.py b/app-cp/schemas/unity_sps.py similarity index 100% rename from app/schemas/unity_sps.py rename to app-cp/schemas/unity_sps.py diff --git a/app-cp/.flake8 b/app/.flake8 similarity index 100% rename from app-cp/.flake8 rename to app/.flake8 diff --git a/app-cp/.gitignore b/app/.gitignore similarity index 100% rename from app-cp/.gitignore rename to app/.gitignore diff --git a/app-cp/.openapi-generator-ignore b/app/.openapi-generator-ignore similarity index 100% rename from app-cp/.openapi-generator-ignore rename to app/.openapi-generator-ignore diff --git a/app-cp/.openapi-generator/FILES b/app/.openapi-generator/FILES similarity index 100% rename from app-cp/.openapi-generator/FILES rename to app/.openapi-generator/FILES diff --git a/app-cp/.openapi-generator/VERSION b/app/.openapi-generator/VERSION similarity index 100% rename from app-cp/.openapi-generator/VERSION rename to app/.openapi-generator/VERSION diff --git a/app-cp/Dockerfile b/app/Dockerfile similarity index 100% rename from app-cp/Dockerfile rename to app/Dockerfile diff --git a/app-cp/README.md b/app/README.md similarity index 100% rename from app-cp/README.md rename to app/README.md diff --git a/app-cp/docker-compose.yaml b/app/docker-compose.yaml similarity index 100% rename from app-cp/docker-compose.yaml rename to app/docker-compose.yaml diff --git a/app-cp/openapi.yaml b/app/openapi.yaml similarity index 100% rename from app-cp/openapi.yaml rename to app/openapi.yaml diff --git a/app-cp/pyproject.toml b/app/pyproject.toml similarity index 100% rename from app-cp/pyproject.toml rename to app/pyproject.toml diff --git a/app-cp/src/openapi_server/utils/__init__.py b/app/src/openapi_server/config/__init__.py similarity index 100% rename from app-cp/src/openapi_server/utils/__init__.py rename to app/src/openapi_server/config/__init__.py diff --git a/app/config.py b/app/src/openapi_server/config/config.py similarity index 100% rename from app/config.py rename to app/src/openapi_server/config/config.py diff --git a/app-cp/src/openapi_server/database/__init__.py b/app/src/openapi_server/database/__init__.py similarity index 100% rename from app-cp/src/openapi_server/database/__init__.py rename to app/src/openapi_server/database/__init__.py diff --git a/app-cp/src/openapi_server/database/crud.py b/app/src/openapi_server/database/crud.py similarity index 95% rename from app-cp/src/openapi_server/database/crud.py rename to app/src/openapi_server/database/crud.py index e88e249..e5e04fd 100644 --- a/app-cp/src/openapi_server/database/crud.py +++ b/app/src/openapi_server/database/crud.py @@ -1,5 +1,4 @@ from sqlalchemy.orm import Session - from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg from . import models @@ -8,9 +7,7 @@ def create_process(db: Session, ogcapppkg: Ogcapppkg): db_process = models.Process(**ogcapppkg.process_description.model_dump()) db_execution_unit = models.ExecutionUnit(**ogcapppkg.execution_unit.model_dump()) - db_ogcapppkg = models.Ogcapppkg( - process=db_process, execution_unit=db_execution_unit - ) + db_ogcapppkg = models.Ogcapppkg(process=db_process, execution_unit=db_execution_unit) db.add(db_ogcapppkg) db.commit() db.refresh(db_ogcapppkg) diff --git a/app-cp/src/openapi_server/database/models.py b/app/src/openapi_server/database/models.py similarity index 96% rename from app-cp/src/openapi_server/database/models.py rename to app/src/openapi_server/database/models.py index c8f06d5..eca7fba 100644 --- a/app-cp/src/openapi_server/database/models.py +++ b/app/src/openapi_server/database/models.py @@ -51,9 +51,7 @@ class ExecutionUnit(Base): class Ogcapppkg(Base): __tablename__ = "ogcapppkgs" _id = Column(Integer, primary_key=True) - process_id = Column( - String, ForeignKey("processes.id", ondelete="CASCADE"), nullable=False - ) + process_id = Column(String, ForeignKey("processes.id", ondelete="CASCADE"), nullable=False) process = relationship("Process", back_populates="ogcapppkg", passive_deletes=True) execution_unit = relationship( "ExecutionUnit", diff --git a/app-cp/src/unity_sps_ogc_processes_api/__init__.py b/app/src/openapi_server/impl/__init__.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/__init__.py rename to app/src/openapi_server/impl/__init__.py diff --git a/app-cp/src/openapi_server/impl/api_api.py b/app/src/openapi_server/impl/api_api.py similarity index 99% rename from app-cp/src/openapi_server/impl/api_api.py rename to app/src/openapi_server/impl/api_api.py index 998b854..e6c02cc 100644 --- a/app-cp/src/openapi_server/impl/api_api.py +++ b/app/src/openapi_server/impl/api_api.py @@ -1,5 +1,4 @@ from fastapi.openapi.utils import get_openapi - from unity_sps_ogc_processes_api.apis.api_api_base import BaseAPIApi from unity_sps_ogc_processes_api.models.enumeration import Enumeration diff --git a/app-cp/src/openapi_server/impl/conformance_api.py b/app/src/openapi_server/impl/conformance_api.py similarity index 100% rename from app-cp/src/openapi_server/impl/conformance_api.py rename to app/src/openapi_server/impl/conformance_api.py diff --git a/app-cp/src/openapi_server/impl/dru_api.py b/app/src/openapi_server/impl/dru_api.py similarity index 85% rename from app-cp/src/openapi_server/impl/dru_api.py rename to app/src/openapi_server/impl/dru_api.py index e1b94d6..0e84744 100644 --- a/app-cp/src/openapi_server/impl/dru_api.py +++ b/app/src/openapi_server/impl/dru_api.py @@ -4,14 +4,13 @@ import requests from fastapi import HTTPException, Response, status +from openapi_server.config.config import Settings +from openapi_server.database import crud +from openapi_server.utils.redis import RedisLock from redis.exceptions import LockError from requests.auth import HTTPBasicAuth from sqlalchemy.orm import Session from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound - -from openapi_server.config.config import Settings -from openapi_server.database import crud -from openapi_server.utils.redis import RedisLock from unity_sps_ogc_processes_api.apis.dru_api_base import BaseDRUApi from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg @@ -42,9 +41,7 @@ def check_process_integrity(db: Session, process_id: str, new_process: bool): class DRUApiImpl(BaseDRUApi): - def __init__( - self, settings: Settings, redis_locking_client: RedisLock, db: Session - ): + def __init__(self, settings: Settings, redis_locking_client: RedisLock, db: Session): self.settings = settings self.redis_locking_client = redis_locking_client self.db = db @@ -53,16 +50,12 @@ def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: lock_key = f"process:{ogcapppkg.process_description.id}" try: with self.redis_locking_client.lock(lock_key, lock_timeout=60): - check_process_integrity( - self.db, ogcapppkg.process_description.id, new_process=True - ) + check_process_integrity(self.db, ogcapppkg.process_description.id, new_process=True) # ogcapppkg.process_description.deployment_status = "deploying" crud.create_process(self.db, ogcapppkg) dag_filename = ogcapppkg.process_description.id + ".py" - dag_catalog_filepath = os.path.join( - self.settings.DAG_CATALOG_DIRECTORY, dag_filename - ) + dag_catalog_filepath = os.path.join(self.settings.DAG_CATALOG_DIRECTORY, dag_filename) if not os.path.isfile(dag_catalog_filepath): existing_files = os.listdir(self.settings.DAG_CATALOG_DIRECTORY) existing_files_str = "\n".join(existing_files) @@ -71,10 +64,8 @@ def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: detail=f"The process ID '{ogcapppkg.process_description.id}' does not have a matching DAG file named '{dag_filename}' in the DAG catalog.\nThe DAG catalog includes the following files:\n{existing_files_str}", ) - if os.path.isfile( - os.path.join(self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename) - ): - # Log warning that file already exists in the deployed dags directory + if os.path.isfile(os.path.join(self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename)): + # TODO Log warning that file already exists in the deployed dags directory pass shutil.copy2( @@ -82,9 +73,7 @@ def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: self.settings.DEPLOYED_DAGS_DIRECTORY, ) - if not os.path.isfile( - os.path.join(self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename) - ): + if not os.path.isfile(os.path.join(self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename)): raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Failed to copy DAG file to deployed directory", @@ -139,9 +128,7 @@ def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: # Re-raise HTTPExceptions without wrapping them raise except Exception as e: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) - ) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: lock_key = f"process:{processId}" @@ -156,18 +143,12 @@ def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: ) # Update the existing process with new data - crud.update_process( - self.db, processId, ogcapppkg.process_description.model_dump() - ) + crud.update_process(self.db, processId, ogcapppkg.process_description.model_dump()) # Update the DAG file dag_filename = f"{processId}.py" - dag_catalog_filepath = os.path.join( - self.settings.DAG_CATALOG_DIRECTORY, dag_filename - ) - deployed_dag_path = os.path.join( - self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename - ) + dag_catalog_filepath = os.path.join(self.settings.DAG_CATALOG_DIRECTORY, dag_filename) + deployed_dag_path = os.path.join(self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename) if os.path.exists(dag_catalog_filepath): shutil.copy2(dag_catalog_filepath, deployed_dag_path) @@ -202,9 +183,7 @@ def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: # Re-raise HTTPExceptions without wrapping them raise except Exception as e: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) - ) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) def undeploy(self, processId: str, force: bool = False) -> None: lock_key = f"process:{processId}" @@ -228,9 +207,7 @@ def undeploy(self, processId: str, force: bool = False) -> None: ) # Pause the DAG - self.pause_dag( - self.settings.EMS_API_URL, processId, ems_api_auth, pause=True - ) + self.pause_dag(self.settings.EMS_API_URL, processId, ems_api_auth, pause=True) # Get active DAG runs again after the DAG is paused active_dag_runs = self.list_active_dag_runs( @@ -253,9 +230,7 @@ def undeploy(self, processId: str, force: bool = False) -> None: # Remove the DAG file from the deployed directory dag_filename = f"{processId}.py" - deployed_dag_path = os.path.join( - self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename - ) + deployed_dag_path = os.path.join(self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename) if os.path.isfile(deployed_dag_path): try: os.remove(deployed_dag_path) @@ -302,9 +277,7 @@ def undeploy(self, processId: str, force: bool = False) -> None: raise except Exception as e: # For any other exception, wrap it in a generic HTTPException - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) - ) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) def pause_dag(self, airflow_url, dag_id, auth, pause=True): endpoint = f"{airflow_url}/dags/{dag_id}" @@ -331,9 +304,9 @@ def stop_task_instances(self, airflow_url, dag_id, dag_run_id, auth): tasks.raise_for_status() for task in tasks.json()["task_instances"]: - task_instance_endpoint = f"{airflow_url}/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task['task_id']}" - update_data = {"dry_run": False, "new_state": "failed"} - update_response = requests.patch( - task_instance_endpoint, auth=auth, json=update_data + task_instance_endpoint = ( + f"{airflow_url}/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task['task_id']}" ) + update_data = {"dry_run": False, "new_state": "failed"} + update_response = requests.patch(task_instance_endpoint, auth=auth, json=update_data) update_response.raise_for_status() diff --git a/app/src/openapi_server/impl/health_api.py b/app/src/openapi_server/impl/health_api.py new file mode 100644 index 0000000..cbffd1e --- /dev/null +++ b/app/src/openapi_server/impl/health_api.py @@ -0,0 +1,7 @@ +from unity_sps_ogc_processes_api.apis.health_api_base import BaseHealthApi +from unity_sps_ogc_processes_api.models.health_check import HealthCheck + + +class HealthApiImpl(BaseHealthApi): + def get_health(self) -> HealthCheck: + return HealthCheck(status="OK") diff --git a/app-cp/src/openapi_server/impl/jobs_api.py b/app/src/openapi_server/impl/jobs_api.py similarity index 89% rename from app-cp/src/openapi_server/impl/jobs_api.py rename to app/src/openapi_server/impl/jobs_api.py index a16ad0c..700ecb0 100644 --- a/app-cp/src/openapi_server/impl/jobs_api.py +++ b/app/src/openapi_server/impl/jobs_api.py @@ -3,16 +3,15 @@ import requests from fastapi import HTTPException, status +from openapi_server.config.config import Settings +from openapi_server.database import crud +from openapi_server.utils.redis import RedisLock from redis.exceptions import LockError # from jsonschema import ValidationError, validate from requests.auth import HTTPBasicAuth from sqlalchemy.orm import Session from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound - -from openapi_server.config.config import Settings -from openapi_server.database import crud -from openapi_server.utils.redis import RedisLock from unity_sps_ogc_processes_api.apis.jobs_api_base import BaseJobsApi from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData from unity_sps_ogc_processes_api.models.job_list import JobList @@ -21,9 +20,7 @@ class JobsApiImpl(BaseJobsApi): - def __init__( - self, settings: Settings, redis_locking_client: RedisLock, db: Session - ): + def __init__(self, settings: Settings, redis_locking_client: RedisLock, db: Session): self.settings = settings self.redis_locking_client = redis_locking_client self.db = db @@ -94,9 +91,7 @@ def dismiss(self, jobId: str) -> StatusInfo: detail=f"Failed to delete DAG run {job.jobID} for DAG {job.processID}: {e}", ) except Exception as e: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) - ) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) def get_jobs(self) -> JobList: jobs = crud.get_jobs(self.db) @@ -129,10 +124,7 @@ def get_result(self, jobId: str, prefer: str) -> Dict[str, InlineOrRefData]: process_lock_key = f"process:{job.processID}" with self.redis_locking_client.lock(process_lock_key): results = crud.get_results(self.db, jobId) - return { - result.name: InlineOrRefData(href=result.href) - for result in results - } + return {result.name: InlineOrRefData(href=result.href) for result in results} except LockError: raise HTTPException( @@ -140,9 +132,7 @@ def get_result(self, jobId: str, prefer: str) -> Dict[str, InlineOrRefData]: detail="Unable to acquire lock. Please try again later.", ) except Exception as e: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) - ) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) def get_status(self, jobId: str) -> StatusInfo: job_lock_key = f"job:{jobId}" @@ -178,9 +168,7 @@ def get_status(self, jobId: str) -> StatusInfo: "failed": StatusCode.FAILED, } data = response.json() - current_execution_status = execution_status_conversion_dict[ - data["state"] - ] + current_execution_status = execution_status_conversion_dict[data["state"]] if job.status != current_execution_status: job.status = current_execution_status job.updated = datetime.now() @@ -189,9 +177,7 @@ def get_status(self, jobId: str) -> StatusInfo: if end_date_str: job.finished = datetime.fromisoformat(end_date_str) - return crud.update_job( - self.db, job.job_id, job.model_dump(by_alias=True) - ) + return crud.update_job(self.db, job.job_id, job.model_dump(by_alias=True)) except LockError: raise HTTPException( @@ -204,6 +190,4 @@ def get_status(self, jobId: str) -> StatusInfo: detail=f"Failed to fetch DAG run {job.job_id} for DAG {job.process_id}: {e}", ) except Exception as e: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) - ) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) diff --git a/app-cp/src/openapi_server/impl/landing_page_api.py b/app/src/openapi_server/impl/landing_page_api.py similarity index 92% rename from app-cp/src/openapi_server/impl/landing_page_api.py rename to app/src/openapi_server/impl/landing_page_api.py index 721d2d6..d3ce484 100644 --- a/app-cp/src/openapi_server/impl/landing_page_api.py +++ b/app/src/openapi_server/impl/landing_page_api.py @@ -9,9 +9,7 @@ def get_landing_page(self, f: str = None) -> LandingPage: title="OGC API - Processes", description="This is the landing page for the OGC API - Processes implementation.", links=[ - Link( - href="/", rel="self", type="application/json", title="This document" - ), + Link(href="/", rel="self", type="application/json", title="This document"), Link( href="/api", # Assuming the API definition is at /api rel="service-desc", diff --git a/app-cp/src/openapi_server/impl/processes_api.py b/app/src/openapi_server/impl/processes_api.py similarity index 86% rename from app-cp/src/openapi_server/impl/processes_api.py rename to app/src/openapi_server/impl/processes_api.py index 0c5b37b..6ceea09 100644 --- a/app-cp/src/openapi_server/impl/processes_api.py +++ b/app/src/openapi_server/impl/processes_api.py @@ -3,14 +3,13 @@ import requests from fastapi import HTTPException, status -from redis.exceptions import LockError -from requests.auth import HTTPBasicAuth -from sqlalchemy.orm import Session - from openapi_server.config.config import Settings from openapi_server.database import crud from openapi_server.impl.dru_api import check_process_integrity from openapi_server.utils.redis import RedisLock +from redis.exceptions import LockError +from requests.auth import HTTPBasicAuth +from sqlalchemy.orm import Session from unity_sps_ogc_processes_api.apis.processes_api_base import BaseProcessesApi from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows @@ -26,9 +25,7 @@ class ProcessesApiImpl(BaseProcessesApi): - def __init__( - self, settings: Settings, redis_locking_client: RedisLock, db: Session - ): + def __init__(self, settings: Settings, redis_locking_client: RedisLock, db: Session): self.settings = settings self.redis_locking_client = redis_locking_client self.db = db @@ -45,28 +42,16 @@ def get_process_description(self, processId: str) -> Process: # Convert metadata, links, inputs, and outputs if they exist metadata = ( - [Metadata.model_validate(m) for m in process.metadata] - if process.metadata - else None - ) - links = ( - [Link.model_validate(link) for link in process.links] - if process.links - else None + [Metadata.model_validate(m) for m in process.metadata] if process.metadata else None ) + links = [Link.model_validate(link) for link in process.links] if process.links else None inputs = ( - { - k: InputDescription.model_validate(v) - for k, v in process.inputs.items() - } + {k: InputDescription.model_validate(v) for k, v in process.inputs.items()} if process.inputs else None ) outputs = ( - { - k: OutputDescription.model_validate(v) - for k, v in process.outputs.items() - } + {k: OutputDescription.model_validate(v) for k, v in process.outputs.items()} if process.outputs else None ) @@ -89,9 +74,7 @@ def get_process_description(self, processId: str) -> Process: detail="Unable to acquire lock. Please try again later.", ) except Exception as e: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) - ) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) def get_processes(self) -> ProcessList: processes = crud.get_processes(self.db) @@ -188,9 +171,7 @@ def execute( # Note: In a real-world scenario, you'd wait for the job to complete # and return the actual results. This is a simplified version. # TODO get result from job using the result endpoint and return it here. - return Execute200Response( - {"result": "Sample output for synchronous execution"} - ) + return Execute200Response({"result": "Sample output for synchronous execution"}) except LockError: raise HTTPException( @@ -199,15 +180,11 @@ def execute( ) except requests.exceptions.RequestException as e: status_code_to_raise = status.HTTP_500_INTERNAL_SERVER_ERROR - detail_message = ( - f"Failed to start DAG run {job_id} with DAG {processId}: {str(e)}" - ) + detail_message = f"Failed to start DAG run {job_id} with DAG {processId}: {str(e)}" if hasattr(e, "response"): detail_message = f"Failed to start DAG run {job_id} with DAG {processId}: {e.response.status_code} {e.response.reason}" raise HTTPException(status_code=status_code_to_raise, detail=detail_message) except Exception as e: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) - ) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/__init__.py b/app/src/openapi_server/utils/__init__.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/apis/__init__.py rename to app/src/openapi_server/utils/__init__.py diff --git a/app-cp/src/openapi_server/utils/redis.py b/app/src/openapi_server/utils/redis.py similarity index 100% rename from app-cp/src/openapi_server/utils/redis.py rename to app/src/openapi_server/utils/redis.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/__init__.py b/app/src/unity_sps_ogc_processes_api/__init__.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/__init__.py rename to app/src/unity_sps_ogc_processes_api/__init__.py diff --git a/app/__init__.py b/app/src/unity_sps_ogc_processes_api/apis/__init__.py similarity index 100% rename from app/__init__.py rename to app/src/unity_sps_ogc_processes_api/apis/__init__.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/api_api.py b/app/src/unity_sps_ogc_processes_api/apis/api_api.py similarity index 99% rename from app-cp/src/unity_sps_ogc_processes_api/apis/api_api.py rename to app/src/unity_sps_ogc_processes_api/apis/api_api.py index 1bbdbdb..bef8770 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/api_api.py +++ b/app/src/unity_sps_ogc_processes_api/apis/api_api.py @@ -3,9 +3,8 @@ import importlib import pkgutil -from fastapi import APIRouter, Query - import openapi_server.impl +from fastapi import APIRouter, Query from unity_sps_ogc_processes_api.apis.api_api_base import BaseAPIApi from unity_sps_ogc_processes_api.models.enumeration import Enumeration from unity_sps_ogc_processes_api.models.exception import Exception diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/api_api_base.py b/app/src/unity_sps_ogc_processes_api/apis/api_api_base.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/apis/api_api_base.py rename to app/src/unity_sps_ogc_processes_api/apis/api_api_base.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api.py b/app/src/unity_sps_ogc_processes_api/apis/conformance_api.py similarity index 99% rename from app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api.py rename to app/src/unity_sps_ogc_processes_api/apis/conformance_api.py index 2aa2bc7..b1470ba 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api.py +++ b/app/src/unity_sps_ogc_processes_api/apis/conformance_api.py @@ -3,9 +3,8 @@ import importlib import pkgutil -from fastapi import APIRouter, Query - import openapi_server.impl +from fastapi import APIRouter, Query from unity_sps_ogc_processes_api.apis.conformance_api_base import BaseConformanceApi from unity_sps_ogc_processes_api.models.conf_classes import ConfClasses from unity_sps_ogc_processes_api.models.exception import Exception diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py b/app/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py rename to app/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py b/app/src/unity_sps_ogc_processes_api/apis/dru_api.py similarity index 89% rename from app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py rename to app/src/unity_sps_ogc_processes_api/apis/dru_api.py index de701fa..a231574 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api.py +++ b/app/src/unity_sps_ogc_processes_api/apis/dru_api.py @@ -3,18 +3,13 @@ import importlib import pkgutil -from fastapi import APIRouter, Body, Depends, Path, Query -from sqlalchemy.orm import Session - import openapi_server.impl +from fastapi import APIRouter, Body, Depends, Path, Query from openapi_server.config.config import Settings from openapi_server.utils.redis import RedisLock +from sqlalchemy.orm import Session from unity_sps_ogc_processes_api.apis.dru_api_base import BaseDRUApi -from unity_sps_ogc_processes_api.dependencies import ( - get_db, - get_redis_locking_client, - get_settings, -) +from unity_sps_ogc_processes_api.dependencies import get_db, get_redis_locking_client, get_settings from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg @@ -44,9 +39,7 @@ async def deploy( settings: Settings = Depends(get_settings), redis_locking_client: RedisLock = Depends(get_redis_locking_client), db: Session = Depends(get_db), - ogcapppkg: Ogcapppkg = Body( - None, description="An OGC Application Package used to deploy a new process." - ), + ogcapppkg: Ogcapppkg = Body(None, description="An OGC Application Package used to deploy a new process."), w: str = Query( None, description="Point to the workflow identifier for deploying a CWL containing multiple workflow definitions", @@ -82,9 +75,7 @@ async def replace( redis_locking_client: RedisLock = Depends(get_redis_locking_client), db: Session = Depends(get_db), processId: str = Path(..., description=""), - ogcapppkg: Ogcapppkg = Body( - None, description="An OGC Application Package used to deploy a new process." - ), + ogcapppkg: Ogcapppkg = Body(None, description="An OGC Application Package used to deploy a new process."), ) -> None: """Replaces a process. For more information, see [Section 6.4](http://docs.ogc.org/DRAFTS/20-044.html#_18582f42-ebc6-4284-9333-c089068f62b6).""" dru_api = BaseDRUApi.subclasses[0](settings, redis_locking_client, db) @@ -115,9 +106,7 @@ async def undeploy( redis_locking_client: RedisLock = Depends(get_redis_locking_client), db: Session = Depends(get_db), processId: str = Path(..., description=""), - force: bool = Query( - False, description="Force undeployment even if there are active DAG runs" - ), + force: bool = Query(False, description="Force undeployment even if there are active DAG runs"), ) -> None: """Undeploys a process. For more information, see [Section 6.5](http://docs.ogc.org/DRAFTS/20-044.html#_16391f9e-538f-4a84-9710-72a6bab82842).""" dru_api = BaseDRUApi.subclasses[0](settings, redis_locking_client, db) diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/dru_api_base.py b/app/src/unity_sps_ogc_processes_api/apis/dru_api_base.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/apis/dru_api_base.py rename to app/src/unity_sps_ogc_processes_api/apis/dru_api_base.py diff --git a/app/src/unity_sps_ogc_processes_api/apis/health_api.py b/app/src/unity_sps_ogc_processes_api/apis/health_api.py new file mode 100644 index 0000000..6df354f --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/apis/health_api.py @@ -0,0 +1,32 @@ +# coding: utf-8 + +import importlib +import pkgutil + +import openapi_server.impl +from fastapi import APIRouter +from unity_sps_ogc_processes_api.apis.health_api_base import BaseHealthApi +from unity_sps_ogc_processes_api.models.exception import Exception +from unity_sps_ogc_processes_api.models.health_check import HealthCheck + +router = APIRouter() + +ns_pkg = openapi_server.impl +for _, name, _ in pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + "."): + importlib.import_module(name) + + +@router.get( + "/health", + responses={ + 200: {"model": HealthCheck, "description": "The health status of the API."}, + 500: {"model": Exception, "description": "A server error occurred."}, + }, + tags=["Health"], + summary="Retrieve the health status of the API.", + response_model_by_alias=True, +) +async def get_health() -> HealthCheck: + """Retrieves the health status of the API.""" + health_api = BaseHealthApi.subclasses[0]() + return health_api.get_health() diff --git a/app/src/unity_sps_ogc_processes_api/apis/health_api_base.py b/app/src/unity_sps_ogc_processes_api/apis/health_api_base.py new file mode 100644 index 0000000..608c74e --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/apis/health_api_base.py @@ -0,0 +1,22 @@ +from abc import ABC, abstractmethod +from typing import ClassVar, Tuple + +from unity_sps_ogc_processes_api.models.health_check import HealthCheck + + +class BaseHealthApi(ABC): + subclasses: ClassVar[Tuple] = () + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + BaseHealthApi.subclasses = BaseHealthApi.subclasses + (cls,) + + @abstractmethod + def get_health(self) -> HealthCheck: + """ + Get the health status of the API. + + Returns: + HealthCheck: The health status of the API. + """ + pass diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api.py b/app/src/unity_sps_ogc_processes_api/apis/jobs_api.py similarity index 97% rename from app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api.py rename to app/src/unity_sps_ogc_processes_api/apis/jobs_api.py index 2903053..75b82f3 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api.py +++ b/app/src/unity_sps_ogc_processes_api/apis/jobs_api.py @@ -4,18 +4,13 @@ import pkgutil from typing import Dict -from fastapi import APIRouter, Depends, Header, Path -from sqlalchemy.orm import Session - import openapi_server.impl +from fastapi import APIRouter, Depends, Header, Path from openapi_server.config.config import Settings from openapi_server.utils.redis import RedisLock +from sqlalchemy.orm import Session from unity_sps_ogc_processes_api.apis.jobs_api_base import BaseJobsApi -from unity_sps_ogc_processes_api.dependencies import ( - get_db, - get_redis_locking_client, - get_settings, -) +from unity_sps_ogc_processes_api.dependencies import get_db, get_redis_locking_client, get_settings from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.extra_models import TokenModel # noqa: F401 from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py b/app/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py rename to app/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api.py b/app/src/unity_sps_ogc_processes_api/apis/landing_page_api.py similarity index 99% rename from app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api.py rename to app/src/unity_sps_ogc_processes_api/apis/landing_page_api.py index b1e8e2c..2454329 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api.py +++ b/app/src/unity_sps_ogc_processes_api/apis/landing_page_api.py @@ -3,9 +3,8 @@ import importlib import pkgutil -from fastapi import APIRouter, Query - import openapi_server.impl +from fastapi import APIRouter, Query from unity_sps_ogc_processes_api.apis.landing_page_api_base import BaseLandingPageApi from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.landing_page import LandingPage diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api_base.py b/app/src/unity_sps_ogc_processes_api/apis/landing_page_api_base.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/apis/landing_page_api_base.py rename to app/src/unity_sps_ogc_processes_api/apis/landing_page_api_base.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api.py b/app/src/unity_sps_ogc_processes_api/apis/processes_api.py similarity index 98% rename from app-cp/src/unity_sps_ogc_processes_api/apis/processes_api.py rename to app/src/unity_sps_ogc_processes_api/apis/processes_api.py index 8354861..8302da8 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api.py +++ b/app/src/unity_sps_ogc_processes_api/apis/processes_api.py @@ -3,18 +3,13 @@ import importlib import pkgutil -from fastapi import APIRouter, Body, Depends, Header, Path, Query -from sqlalchemy.orm import Session - import openapi_server.impl +from fastapi import APIRouter, Body, Depends, Header, Path, Query from openapi_server.config.config import Settings from openapi_server.utils.redis import RedisLock +from sqlalchemy.orm import Session from unity_sps_ogc_processes_api.apis.processes_api_base import BaseProcessesApi -from unity_sps_ogc_processes_api.dependencies import ( - get_db, - get_redis_locking_client, - get_settings, -) +from unity_sps_ogc_processes_api.dependencies import get_db, get_redis_locking_client, get_settings from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows diff --git a/app-cp/src/unity_sps_ogc_processes_api/apis/processes_api_base.py b/app/src/unity_sps_ogc_processes_api/apis/processes_api_base.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/apis/processes_api_base.py rename to app/src/unity_sps_ogc_processes_api/apis/processes_api_base.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/dependencies.py b/app/src/unity_sps_ogc_processes_api/dependencies.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/dependencies.py rename to app/src/unity_sps_ogc_processes_api/dependencies.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/main.py b/app/src/unity_sps_ogc_processes_api/main.py similarity index 82% rename from app-cp/src/unity_sps_ogc_processes_api/main.py rename to app/src/unity_sps_ogc_processes_api/main.py index 1dce037..b86882e 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/main.py +++ b/app/src/unity_sps_ogc_processes_api/main.py @@ -14,23 +14,15 @@ from fastapi import Depends, FastAPI - from openapi_server.database import engine, models from unity_sps_ogc_processes_api.apis.api_api import router as APIApiRouter -from unity_sps_ogc_processes_api.apis.conformance_api import ( - router as ConformanceApiRouter, -) +from unity_sps_ogc_processes_api.apis.conformance_api import router as ConformanceApiRouter from unity_sps_ogc_processes_api.apis.dru_api import router as DRUApiRouter +from unity_sps_ogc_processes_api.apis.health_api import router as HealthApiRouter from unity_sps_ogc_processes_api.apis.jobs_api import router as JobsApiRouter -from unity_sps_ogc_processes_api.apis.landing_page_api import ( - router as LandingPageApiRouter, -) +from unity_sps_ogc_processes_api.apis.landing_page_api import router as LandingPageApiRouter from unity_sps_ogc_processes_api.apis.processes_api import router as ProcessesApiRouter -from unity_sps_ogc_processes_api.dependencies import ( - get_db, - get_redis_locking_client, - get_settings, -) +from unity_sps_ogc_processes_api.dependencies import get_db, get_redis_locking_client, get_settings # Create database tables models.Base.metadata.create_all(bind=engine) @@ -59,3 +51,4 @@ app.include_router(JobsApiRouter) app.include_router(LandingPageApiRouter) app.include_router(ProcessesApiRouter) +app.include_router(HealthApiRouter) diff --git a/app/schemas/__init__.py b/app/src/unity_sps_ogc_processes_api/models/__init__.py similarity index 100% rename from app/schemas/__init__.py rename to app/src/unity_sps_ogc_processes_api/models/__init__.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/bbox.py b/app/src/unity_sps_ogc_processes_api/models/bbox.py similarity index 93% rename from app-cp/src/unity_sps_ogc_processes_api/models/bbox.py rename to app/src/unity_sps_ogc_processes_api/models/bbox.py index 2c49ce6..f211f68 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/bbox.py +++ b/app/src/unity_sps_ogc_processes_api/models/bbox.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional, Union from pydantic import BaseModel, StrictFloat, StrictInt - from unity_sps_ogc_processes_api.models.bbox_def_crs import BboxDefCrs try: @@ -91,11 +90,7 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { "bbox": obj.get("bbox"), - "crs": ( - BboxDefCrs.from_dict(obj.get("crs")) - if obj.get("crs") is not None - else None - ), + "crs": (BboxDefCrs.from_dict(obj.get("crs")) if obj.get("crs") is not None else None), } ) return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/bbox1.py b/app/src/unity_sps_ogc_processes_api/models/bbox1.py similarity index 93% rename from app-cp/src/unity_sps_ogc_processes_api/models/bbox1.py rename to app/src/unity_sps_ogc_processes_api/models/bbox1.py index c85ff66..6c47853 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/bbox1.py +++ b/app/src/unity_sps_ogc_processes_api/models/bbox1.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional, Union from pydantic import BaseModel, StrictFloat, StrictInt - from unity_sps_ogc_processes_api.models.bbox_def_crs import BboxDefCrs try: @@ -91,11 +90,7 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { "bbox": obj.get("bbox"), - "crs": ( - BboxDefCrs.from_dict(obj.get("crs")) - if obj.get("crs") is not None - else None - ), + "crs": (BboxDefCrs.from_dict(obj.get("crs")) if obj.get("crs") is not None else None), } ) return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py b/app/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py similarity index 89% rename from app-cp/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py rename to app/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py index 9c415b7..d230477 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py +++ b/app/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py @@ -37,13 +37,9 @@ class BboxDefCrs(BaseModel): """ # data type: str - anyof_schema_1_validator: Optional[StrictStr] = ( - "http://www.opengis.net/def/crs/OGC/1.3/CRS84" - ) + anyof_schema_1_validator: Optional[StrictStr] = "http://www.opengis.net/def/crs/OGC/1.3/CRS84" # data type: str - anyof_schema_2_validator: Optional[StrictStr] = ( - "http://www.opengis.net/def/crs/OGC/1.3/CRS84" - ) + anyof_schema_2_validator: Optional[StrictStr] = "http://www.opengis.net/def/crs/OGC/1.3/CRS84" if TYPE_CHECKING: actual_instance: Optional[Union[str]] = None else: @@ -58,13 +54,9 @@ class BboxDefCrs(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/bbox_processes.py b/app/src/unity_sps_ogc_processes_api/models/bbox_processes.py similarity index 93% rename from app-cp/src/unity_sps_ogc_processes_api/models/bbox_processes.py rename to app/src/unity_sps_ogc_processes_api/models/bbox_processes.py index 0f5a692..d0a45a8 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/bbox_processes.py +++ b/app/src/unity_sps_ogc_processes_api/models/bbox_processes.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional, Union from pydantic import BaseModel, StrictFloat, StrictInt - from unity_sps_ogc_processes_api.models.bbox_def_crs import BboxDefCrs try: @@ -91,11 +90,7 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { "bbox": obj.get("bbox"), - "crs": ( - BboxDefCrs.from_dict(obj.get("crs")) - if obj.get("crs") is not None - else None - ), + "crs": (BboxDefCrs.from_dict(obj.get("crs")) if obj.get("crs") is not None else None), } ) return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/collection_info.py b/app/src/unity_sps_ogc_processes_api/models/collection_info.py similarity index 86% rename from app-cp/src/unity_sps_ogc_processes_api/models/collection_info.py rename to app/src/unity_sps_ogc_processes_api/models/collection_info.py index 72bece4..af90b89 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/collection_info.py +++ b/app/src/unity_sps_ogc_processes_api/models/collection_info.py @@ -22,10 +22,7 @@ from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr from typing_extensions import Annotated - -from unity_sps_ogc_processes_api.models.collection_info_data_type import ( - CollectionInfoDataType, -) +from unity_sps_ogc_processes_api.models.collection_info_data_type import CollectionInfoDataType from unity_sps_ogc_processes_api.models.extent_uad import ExtentUad from unity_sps_ogc_processes_api.models.link import Link @@ -40,12 +37,8 @@ class CollectionInfo(BaseModel): CollectionInfo """ # noqa: E501 - id: StrictStr = Field( - description="identifier of the collection used, for example, in URIs" - ) - title: Optional[StrictStr] = Field( - default=None, description="human readable title of the collection" - ) + id: StrictStr = Field(description="identifier of the collection used, for example, in URIs") + title: Optional[StrictStr] = Field(default=None, description="human readable title of the collection") description: Optional[StrictStr] = Field( default=None, description="a description of the data in the collection" ) @@ -61,12 +54,10 @@ class CollectionInfo(BaseModel): description="the list of coordinate reference systems supported by the API; the first item is the default coordinate reference system", ) data_type: Optional[CollectionInfoDataType] = Field(default=None, alias="dataType") - geometry_dimension: Optional[Annotated[int, Field(le=3, strict=True, ge=0)]] = ( - Field( - default=None, - description="The geometry dimension of the features shown in this layer (0: points, 1: curves, 2: surfaces, 3: solids), unspecified: mixed or unknown", - alias="geometryDimension", - ) + geometry_dimension: Optional[Annotated[int, Field(le=3, strict=True, ge=0)]] = Field( + default=None, + description="The geometry dimension of the features shown in this layer (0: points, 1: curves, 2: surfaces, 3: solids), unspecified: mixed or unknown", + alias="geometryDimension", ) min_scale_denominator: Optional[Union[StrictFloat, StrictInt]] = Field( default=None, @@ -173,16 +164,8 @@ def from_dict(cls, obj: Dict) -> Self: if obj.get("links") is not None else None ), - "extent": ( - ExtentUad.from_dict(obj.get("extent")) - if obj.get("extent") is not None - else None - ), - "itemType": ( - obj.get("itemType") - if obj.get("itemType") is not None - else "unknown" - ), + "extent": (ExtentUad.from_dict(obj.get("extent")) if obj.get("extent") is not None else None), + "itemType": (obj.get("itemType") if obj.get("itemType") is not None else "unknown"), "crs": obj.get("crs"), "dataType": ( CollectionInfoDataType.from_dict(obj.get("dataType")) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/collection_info_data_type.py b/app/src/unity_sps_ogc_processes_api/models/collection_info_data_type.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/collection_info_data_type.py rename to app/src/unity_sps_ogc_processes_api/models/collection_info_data_type.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/collections.py b/app/src/unity_sps_ogc_processes_api/models/collections.py similarity index 96% rename from app-cp/src/unity_sps_ogc_processes_api/models/collections.py rename to app/src/unity_sps_ogc_processes_api/models/collections.py index 4dd6775..d2d852b 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/collections.py +++ b/app/src/unity_sps_ogc_processes_api/models/collections.py @@ -23,7 +23,6 @@ from pydantic import BaseModel, Field from typing_extensions import Annotated - from unity_sps_ogc_processes_api.models.collection_info import CollectionInfo from unity_sps_ogc_processes_api.models.link import Link @@ -126,10 +125,7 @@ def from_dict(cls, obj: Dict) -> Self: "numberMatched": obj.get("numberMatched"), "numberReturned": obj.get("numberReturned"), "collections": ( - [ - CollectionInfo.from_dict(_item) - for _item in obj.get("collections") - ] + [CollectionInfo.from_dict(_item) for _item in obj.get("collections")] if obj.get("collections") is not None else None ), diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/conf_classes.py b/app/src/unity_sps_ogc_processes_api/models/conf_classes.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/conf_classes.py rename to app/src/unity_sps_ogc_processes_api/models/conf_classes.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/crs.py b/app/src/unity_sps_ogc_processes_api/models/crs.py similarity index 94% rename from app-cp/src/unity_sps_ogc_processes_api/models/crs.py rename to app/src/unity_sps_ogc_processes_api/models/crs.py index 47a3fab..5dbcee6 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/crs.py +++ b/app/src/unity_sps_ogc_processes_api/models/crs.py @@ -22,7 +22,6 @@ from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator from typing_extensions import Literal - from unity_sps_ogc_processes_api.models.crs_one_of import CrsOneOf try: @@ -56,13 +55,9 @@ class Crs(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of.py b/app/src/unity_sps_ogc_processes_api/models/crs_one_of.py similarity index 87% rename from app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of.py rename to app/src/unity_sps_ogc_processes_api/models/crs_one_of.py index e059e18..b037cd3 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of.py +++ b/app/src/unity_sps_ogc_processes_api/models/crs_one_of.py @@ -22,7 +22,6 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal - from unity_sps_ogc_processes_api.models.crs_one_of_one_of import CrsOneOfOneOf from unity_sps_ogc_processes_api.models.crs_one_of_one_of1 import CrsOneOfOneOf1 from unity_sps_ogc_processes_api.models.crs_one_of_one_of2 import CrsOneOfOneOf2 @@ -46,12 +45,8 @@ class CrsOneOf(BaseModel): oneof_schema_2_validator: Optional[CrsOneOfOneOf1] = None # data type: CrsOneOfOneOf2 oneof_schema_3_validator: Optional[CrsOneOfOneOf2] = None - actual_instance: Optional[Union[CrsOneOfOneOf, CrsOneOfOneOf1, CrsOneOfOneOf2]] = ( - None - ) - one_of_schemas: List[str] = Literal[ - "CrsOneOfOneOf", "CrsOneOfOneOf1", "CrsOneOfOneOf2" - ] + actual_instance: Optional[Union[CrsOneOfOneOf, CrsOneOfOneOf1, CrsOneOfOneOf2]] = None + one_of_schemas: List[str] = Literal["CrsOneOfOneOf", "CrsOneOfOneOf1", "CrsOneOfOneOf2"] model_config = { "validate_assignment": True, @@ -61,13 +56,9 @@ class CrsOneOf(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -79,23 +70,17 @@ def actual_instance_must_validate_oneof(cls, v): match = 0 # validate data type: CrsOneOfOneOf if not isinstance(v, CrsOneOfOneOf): - error_messages.append( - f"Error! Input type `{type(v)}` is not `CrsOneOfOneOf`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `CrsOneOfOneOf`") else: match += 1 # validate data type: CrsOneOfOneOf1 if not isinstance(v, CrsOneOfOneOf1): - error_messages.append( - f"Error! Input type `{type(v)}` is not `CrsOneOfOneOf1`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `CrsOneOfOneOf1`") else: match += 1 # validate data type: CrsOneOfOneOf2 if not isinstance(v, CrsOneOfOneOf2): - error_messages.append( - f"Error! Input type `{type(v)}` is not `CrsOneOfOneOf2`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `CrsOneOfOneOf2`") else: match += 1 if match > 1: diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py b/app/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py similarity index 95% rename from app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py rename to app/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py index bb018ae..7adcd6f 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py +++ b/app/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py @@ -33,9 +33,7 @@ class CrsOneOfOneOf(BaseModel): CrsOneOfOneOf """ # noqa: E501 - uri: StrictStr = Field( - description="Reference to one coordinate reference system (CRS)" - ) + uri: StrictStr = Field(description="Reference to one coordinate reference system (CRS)") __properties: ClassVar[List[str]] = ["uri"] model_config = { diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of1.py b/app/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of1.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of1.py rename to app/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of1.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of2.py b/app/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of2.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of2.py rename to app/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of2.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/data_type.py b/app/src/unity_sps_ogc_processes_api/models/data_type.py similarity index 94% rename from app-cp/src/unity_sps_ogc_processes_api/models/data_type.py rename to app/src/unity_sps_ogc_processes_api/models/data_type.py index c530760..55755a0 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/data_type.py +++ b/app/src/unity_sps_ogc_processes_api/models/data_type.py @@ -51,13 +51,9 @@ class DataType(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/description_type.py b/app/src/unity_sps_ogc_processes_api/models/description_type.py similarity index 99% rename from app-cp/src/unity_sps_ogc_processes_api/models/description_type.py rename to app/src/unity_sps_ogc_processes_api/models/description_type.py index 305fecf..62c1729 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/description_type.py +++ b/app/src/unity_sps_ogc_processes_api/models/description_type.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, StrictStr - from unity_sps_ogc_processes_api.models.metadata import Metadata try: diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/enumeration.py b/app/src/unity_sps_ogc_processes_api/models/enumeration.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/enumeration.py rename to app/src/unity_sps_ogc_processes_api/models/enumeration.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/exception.py b/app/src/unity_sps_ogc_processes_api/models/exception.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/exception.py rename to app/src/unity_sps_ogc_processes_api/models/exception.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/execute.py b/app/src/unity_sps_ogc_processes_api/models/execute.py similarity index 88% rename from app-cp/src/unity_sps_ogc_processes_api/models/execute.py rename to app/src/unity_sps_ogc_processes_api/models/execute.py index ce1da08..7218502 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/execute.py +++ b/app/src/unity_sps_ogc_processes_api/models/execute.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel - from unity_sps_ogc_processes_api.models.input import Input from unity_sps_ogc_processes_api.models.output import Output from unity_sps_ogc_processes_api.models.subscriber import Subscriber @@ -108,25 +107,17 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { "inputs": ( - dict( - (_k, Input.from_dict(_v)) - for _k, _v in obj.get("inputs").items() - ) + dict((_k, Input.from_dict(_v)) for _k, _v in obj.get("inputs").items()) if obj.get("inputs") is not None else None ), "outputs": ( - dict( - (_k, Output.from_dict(_v)) - for _k, _v in obj.get("outputs").items() - ) + dict((_k, Output.from_dict(_v)) for _k, _v in obj.get("outputs").items()) if obj.get("outputs") is not None else None ), "subscriber": ( - Subscriber.from_dict(obj.get("subscriber")) - if obj.get("subscriber") is not None - else None + Subscriber.from_dict(obj.get("subscriber")) if obj.get("subscriber") is not None else None ), } ) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/execute200_response.py b/app/src/unity_sps_ogc_processes_api/models/execute200_response.py similarity index 93% rename from app-cp/src/unity_sps_ogc_processes_api/models/execute200_response.py rename to app/src/unity_sps_ogc_processes_api/models/execute200_response.py index 77f0081..320b91f 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/execute200_response.py +++ b/app/src/unity_sps_ogc_processes_api/models/execute200_response.py @@ -19,7 +19,6 @@ from typing import Any, Dict, List, Union from pydantic import FilePath, RootModel, field_validator - from unity_sps_ogc_processes_api.models.bbox import Bbox from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.qualified_input_value import QualifiedInputValue @@ -50,9 +49,7 @@ def validate_root(cls, v): if isinstance(v, dict): for key, value in v.items(): if not isinstance(key, str): - raise ValueError( - f"Dictionary keys must be strings, got {type(key)}" - ) + raise ValueError(f"Dictionary keys must be strings, got {type(key)}") if not isinstance( value, (Bbox, list, bool, float, int, str, Link, QualifiedInputValue), diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/execute200_response1.py b/app/src/unity_sps_ogc_processes_api/models/execute200_response1.py similarity index 93% rename from app-cp/src/unity_sps_ogc_processes_api/models/execute200_response1.py rename to app/src/unity_sps_ogc_processes_api/models/execute200_response1.py index 7392912..d802421 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/execute200_response1.py +++ b/app/src/unity_sps_ogc_processes_api/models/execute200_response1.py @@ -20,16 +20,8 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import ( - BaseModel, - Field, - StrictFloat, - StrictInt, - StrictStr, - field_validator, -) +from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator from typing_extensions import Annotated - from unity_sps_ogc_processes_api.models.geo_json_feature import GeoJSONFeature try: @@ -45,9 +37,7 @@ class Execute200Response1(BaseModel): type: StrictStr features: List[GeoJSONFeature] - bbox: Optional[ - Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] - ] = None + bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None __properties: ClassVar[List[str]] = ["type", "features", "bbox"] @field_validator("type") diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/execute_workflows.py b/app/src/unity_sps_ogc_processes_api/models/execute_workflows.py similarity index 90% rename from app-cp/src/unity_sps_ogc_processes_api/models/execute_workflows.py rename to app/src/unity_sps_ogc_processes_api/models/execute_workflows.py index fbe895c..b526f16 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/execute_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/execute_workflows.py @@ -21,10 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - -from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( - FieldsModifiersProperties, -) +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import FieldsModifiersProperties from unity_sps_ogc_processes_api.models.input_workflows import InputWorkflows from unity_sps_ogc_processes_api.models.output_workflows import OutputWorkflows from unity_sps_ogc_processes_api.models.subscriber import Subscriber @@ -137,25 +134,17 @@ def from_dict(cls, obj: Dict) -> Self: "sortBy": obj.get("sortBy"), "process": obj.get("process"), "inputs": ( - dict( - (_k, InputWorkflows.from_dict(_v)) - for _k, _v in obj.get("inputs").items() - ) + dict((_k, InputWorkflows.from_dict(_v)) for _k, _v in obj.get("inputs").items()) if obj.get("inputs") is not None else None ), "outputs": ( - dict( - (_k, OutputWorkflows.from_dict(_v)) - for _k, _v in obj.get("outputs").items() - ) + dict((_k, OutputWorkflows.from_dict(_v)) for _k, _v in obj.get("outputs").items()) if obj.get("outputs") is not None else None ), "subscriber": ( - Subscriber.from_dict(obj.get("subscriber")) - if obj.get("subscriber") is not None - else None + Subscriber.from_dict(obj.get("subscriber")) if obj.get("subscriber") is not None else None ), } ) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/execute_workflows1.py b/app/src/unity_sps_ogc_processes_api/models/execute_workflows1.py similarity index 90% rename from app-cp/src/unity_sps_ogc_processes_api/models/execute_workflows1.py rename to app/src/unity_sps_ogc_processes_api/models/execute_workflows1.py index 0336057..c060c4e 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/execute_workflows1.py +++ b/app/src/unity_sps_ogc_processes_api/models/execute_workflows1.py @@ -21,10 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - -from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( - FieldsModifiersProperties, -) +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import FieldsModifiersProperties from unity_sps_ogc_processes_api.models.input_workflows1 import InputWorkflows1 from unity_sps_ogc_processes_api.models.output_workflows1 import OutputWorkflows1 from unity_sps_ogc_processes_api.models.subscriber import Subscriber @@ -137,25 +134,17 @@ def from_dict(cls, obj: Dict) -> Self: "sortBy": obj.get("sortBy"), "process": obj.get("process"), "inputs": ( - dict( - (_k, InputWorkflows1.from_dict(_v)) - for _k, _v in obj.get("inputs").items() - ) + dict((_k, InputWorkflows1.from_dict(_v)) for _k, _v in obj.get("inputs").items()) if obj.get("inputs") is not None else None ), "outputs": ( - dict( - (_k, OutputWorkflows1.from_dict(_v)) - for _k, _v in obj.get("outputs").items() - ) + dict((_k, OutputWorkflows1.from_dict(_v)) for _k, _v in obj.get("outputs").items()) if obj.get("outputs") is not None else None ), "subscriber": ( - Subscriber.from_dict(obj.get("subscriber")) - if obj.get("subscriber") is not None - else None + Subscriber.from_dict(obj.get("subscriber")) if obj.get("subscriber") is not None else None ), } ) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/execution_unit.py b/app/src/unity_sps_ogc_processes_api/models/execution_unit.py similarity index 94% rename from app-cp/src/unity_sps_ogc_processes_api/models/execution_unit.py rename to app/src/unity_sps_ogc_processes_api/models/execution_unit.py index db04341..a333153 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/execution_unit.py +++ b/app/src/unity_sps_ogc_processes_api/models/execution_unit.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr, field_validator - from unity_sps_ogc_processes_api.models.execution_unit_config import ExecutionUnitConfig try: @@ -36,9 +35,7 @@ class ExecutionUnit(BaseModel): """ # noqa: E501 type: StrictStr = Field(description="Type of execution unit.") - image: StrictStr = Field( - description="Container image reference for the execution unit." - ) + image: StrictStr = Field(description="Container image reference for the execution unit.") deployment: Optional[StrictStr] = Field( default=None, description="Deployment information for the execution unit." ) @@ -60,9 +57,7 @@ def deployment_validate_enum(cls, value): return value if value not in ("local", "remote", "hpc", "cloud"): - raise ValueError( - "must be one of enum values ('local', 'remote', 'hpc', 'cloud')" - ) + raise ValueError("must be one of enum values ('local', 'remote', 'hpc', 'cloud')") return value model_config = { diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/execution_unit_config.py b/app/src/unity_sps_ogc_processes_api/models/execution_unit_config.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/execution_unit_config.py rename to app/src/unity_sps_ogc_processes_api/models/execution_unit_config.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent.py b/app/src/unity_sps_ogc_processes_api/models/extent.py similarity index 93% rename from app-cp/src/unity_sps_ogc_processes_api/models/extent.py rename to app/src/unity_sps_ogc_processes_api/models/extent.py index f5f48ee..a335b27 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/extent.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel - from unity_sps_ogc_processes_api.models.extent_spatial import ExtentSpatial from unity_sps_ogc_processes_api.models.extent_temporal import ExtentTemporal @@ -95,14 +94,10 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { "spatial": ( - ExtentSpatial.from_dict(obj.get("spatial")) - if obj.get("spatial") is not None - else None + ExtentSpatial.from_dict(obj.get("spatial")) if obj.get("spatial") is not None else None ), "temporal": ( - ExtentTemporal.from_dict(obj.get("temporal")) - if obj.get("temporal") is not None - else None + ExtentTemporal.from_dict(obj.get("temporal")) if obj.get("temporal") is not None else None ), } ) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial.py b/app/src/unity_sps_ogc_processes_api/models/extent_spatial.py similarity index 89% rename from app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial.py rename to app/src/unity_sps_ogc_processes_api/models/extent_spatial.py index 15666e6..90d36c9 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_spatial.py @@ -20,19 +20,9 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import ( - BaseModel, - Field, - StrictFloat, - StrictInt, - StrictStr, - field_validator, -) +from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator from typing_extensions import Annotated - -from unity_sps_ogc_processes_api.models.extent_spatial_grid_inner import ( - ExtentSpatialGridInner, -) +from unity_sps_ogc_processes_api.models.extent_spatial_grid_inner import ExtentSpatialGridInner try: from typing import Self @@ -45,9 +35,7 @@ class ExtentSpatial(BaseModel): The spatial extent of the data in the collection. """ # noqa: E501 - bbox: Optional[ - Annotated[List[List[Union[StrictFloat, StrictInt]]], Field(min_length=1)] - ] = Field( + bbox: Optional[Annotated[List[List[Union[StrictFloat, StrictInt]]], Field(min_length=1)]] = Field( default=None, description="One or more bounding boxes that describe the spatial extent of the dataset. In the Core only a single bounding box is supported. Extensions may support additional areas. The first bounding box describes the overall spatial extent of the data. All subsequent bounding boxes describe more precise bounding boxes, e.g., to identify clusters of data. Clients only interested in the overall spatial extent will only need to access the first item in each array.", ) @@ -55,9 +43,7 @@ class ExtentSpatial(BaseModel): default="1.3/CRS84", description="Coordinate reference system of the coordinates in the spatial extent (property `bbox`). The default reference system is WGS 84 longitude/latitude. In the Core the only other supported coordinate reference system is WGS 84 longitude/latitude/ellipsoidal height for coordinates with height. Extensions may support additional coordinate reference systems and add additional enum values.", ) - grid: Optional[ - Annotated[List[ExtentSpatialGridInner], Field(min_length=2, max_length=3)] - ] = Field( + grid: Optional[Annotated[List[ExtentSpatialGridInner], Field(min_length=2, max_length=3)]] = Field( default=None, description="Provides information about the limited availability of data within the collection organized as a grid (regular or irregular) along each spatial dimension.", ) @@ -136,10 +122,7 @@ def from_dict(cls, obj: Dict) -> Self: "bbox": obj.get("bbox"), "crs": obj.get("crs") if obj.get("crs") is not None else "1.3/CRS84", "grid": ( - [ - ExtentSpatialGridInner.from_dict(_item) - for _item in obj.get("grid") - ] + [ExtentSpatialGridInner.from_dict(_item) for _item in obj.get("grid")] if obj.get("grid") is not None else None ), diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py similarity index 93% rename from app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py rename to app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py index d943b10..8caf637 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py @@ -22,7 +22,6 @@ from pydantic import BaseModel, Field, StrictInt from typing_extensions import Annotated - from unity_sps_ogc_processes_api.models.extent_spatial_grid_inner_coordinates_inner import ( ExtentSpatialGridInnerCoordinatesInner, ) @@ -41,11 +40,11 @@ class ExtentSpatialGridInner(BaseModel): ExtentSpatialGridInner """ # noqa: E501 - coordinates: Optional[ - Annotated[List[ExtentSpatialGridInnerCoordinatesInner], Field(min_length=1)] - ] = Field( - default=None, - description="List of coordinates along the dimension for which data organized as an irregular grid in the collection is available (e.g., 2, 10, 80, 100).", + coordinates: Optional[Annotated[List[ExtentSpatialGridInnerCoordinatesInner], Field(min_length=1)]] = ( + Field( + default=None, + description="List of coordinates along the dimension for which data organized as an irregular grid in the collection is available (e.g., 2, 10, 80, 100).", + ) ) cells_count: Optional[StrictInt] = Field( default=None, diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py similarity index 92% rename from app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py rename to app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py index 40e0685..3d0e20b 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py @@ -20,14 +20,7 @@ import re # noqa: F401 from typing import Dict, List, Optional, Union -from pydantic import ( - BaseModel, - StrictFloat, - StrictInt, - StrictStr, - ValidationError, - field_validator, -) +from pydantic import BaseModel, StrictFloat, StrictInt, StrictStr, ValidationError, field_validator from typing_extensions import Literal try: @@ -58,13 +51,9 @@ class ExtentSpatialGridInnerCoordinatesInner(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py similarity index 92% rename from app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py rename to app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py index 4f87b6c..51448a4 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py @@ -20,14 +20,7 @@ import re # noqa: F401 from typing import Dict, List, Optional, Union -from pydantic import ( - BaseModel, - StrictFloat, - StrictInt, - StrictStr, - ValidationError, - field_validator, -) +from pydantic import BaseModel, StrictFloat, StrictInt, StrictStr, ValidationError, field_validator from typing_extensions import Literal try: @@ -58,13 +51,9 @@ class ExtentSpatialGridInnerResolution(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal.py b/app/src/unity_sps_ogc_processes_api/models/extent_temporal.py similarity index 95% rename from app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal.py rename to app/src/unity_sps_ogc_processes_api/models/extent_temporal.py index a30bbec..77d4618 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_temporal.py @@ -23,7 +23,6 @@ from pydantic import BaseModel, Field, StrictStr, field_validator from typing_extensions import Annotated - from unity_sps_ogc_processes_api.models.extent_temporal_grid import ExtentTemporalGrid try: @@ -39,9 +38,7 @@ class ExtentTemporal(BaseModel): interval: Optional[ Annotated[ - List[ - Annotated[List[Optional[datetime]], Field(min_length=2, max_length=2)] - ], + List[Annotated[List[Optional[datetime]], Field(min_length=2, max_length=2)]], Field(min_length=1), ] ] = Field( @@ -125,9 +122,7 @@ def from_dict(cls, obj: Dict) -> Self: else "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian" ), "grid": ( - ExtentTemporalGrid.from_dict(obj.get("grid")) - if obj.get("grid") is not None - else None + ExtentTemporalGrid.from_dict(obj.get("grid")) if obj.get("grid") is not None else None ), } ) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py b/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py similarity index 90% rename from app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py rename to app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py index 8b24a5b..ad1bad5 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py @@ -22,10 +22,7 @@ from pydantic import BaseModel, Field, StrictInt, StrictStr from typing_extensions import Annotated - -from unity_sps_ogc_processes_api.models.extent_temporal_grid_resolution import ( - ExtentTemporalGridResolution, -) +from unity_sps_ogc_processes_api.models.extent_temporal_grid_resolution import ExtentTemporalGridResolution try: from typing import Self @@ -38,11 +35,9 @@ class ExtentTemporalGrid(BaseModel): Provides information about the limited availability of data within the collection organized as a grid (regular or irregular) along the temporal dimension. """ # noqa: E501 - coordinates: Optional[Annotated[List[Optional[StrictStr]], Field(min_length=1)]] = ( - Field( - default=None, - description='List of coordinates along the temporal dimension for which data organized as an irregular grid in the collection is available (e.g., "2017-11-14T09:00Z","2017-11-14T12:00Z","2017-11-14T15:00Z","2017-11-14T18:00Z","2017-11-14T21:00Z").', - ) + coordinates: Optional[Annotated[List[Optional[StrictStr]], Field(min_length=1)]] = Field( + default=None, + description='List of coordinates along the temporal dimension for which data organized as an irregular grid in the collection is available (e.g., "2017-11-14T09:00Z","2017-11-14T12:00Z","2017-11-14T15:00Z","2017-11-14T18:00Z","2017-11-14T21:00Z").', ) cells_count: Optional[StrictInt] = Field( default=None, diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py b/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py similarity index 92% rename from app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py rename to app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py index 6164e15..b08ed8e 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py @@ -20,14 +20,7 @@ import re # noqa: F401 from typing import Dict, List, Optional, Union -from pydantic import ( - BaseModel, - StrictFloat, - StrictInt, - StrictStr, - ValidationError, - field_validator, -) +from pydantic import BaseModel, StrictFloat, StrictInt, StrictStr, ValidationError, field_validator from typing_extensions import Literal try: @@ -58,13 +51,9 @@ class ExtentTemporalGridResolution(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extent_uad.py b/app/src/unity_sps_ogc_processes_api/models/extent_uad.py similarity index 93% rename from app-cp/src/unity_sps_ogc_processes_api/models/extent_uad.py rename to app/src/unity_sps_ogc_processes_api/models/extent_uad.py index 6a09966..60b7bda 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/extent_uad.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_uad.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel - from unity_sps_ogc_processes_api.models.extent_spatial import ExtentSpatial from unity_sps_ogc_processes_api.models.extent_temporal import ExtentTemporal @@ -95,14 +94,10 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { "spatial": ( - ExtentSpatial.from_dict(obj.get("spatial")) - if obj.get("spatial") is not None - else None + ExtentSpatial.from_dict(obj.get("spatial")) if obj.get("spatial") is not None else None ), "temporal": ( - ExtentTemporal.from_dict(obj.get("temporal")) - if obj.get("temporal") is not None - else None + ExtentTemporal.from_dict(obj.get("temporal")) if obj.get("temporal") is not None else None ), } ) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/extra_models.py b/app/src/unity_sps_ogc_processes_api/models/extra_models.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/extra_models.py rename to app/src/unity_sps_ogc_processes_api/models/extra_models.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/feature_collection.py b/app/src/unity_sps_ogc_processes_api/models/feature_collection.py similarity index 93% rename from app-cp/src/unity_sps_ogc_processes_api/models/feature_collection.py rename to app/src/unity_sps_ogc_processes_api/models/feature_collection.py index d9d2f08..75289b9 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/feature_collection.py +++ b/app/src/unity_sps_ogc_processes_api/models/feature_collection.py @@ -20,16 +20,8 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import ( - BaseModel, - Field, - StrictFloat, - StrictInt, - StrictStr, - field_validator, -) +from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator from typing_extensions import Annotated - from unity_sps_ogc_processes_api.models.geo_json_feature import GeoJSONFeature try: @@ -45,9 +37,7 @@ class FeatureCollection(BaseModel): type: StrictStr features: List[GeoJSONFeature] - bbox: Optional[ - Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] - ] = None + bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None __properties: ClassVar[List[str]] = ["type", "features", "bbox"] @field_validator("type") diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers.py b/app/src/unity_sps_ogc_processes_api/models/fields_modifiers.py similarity index 98% rename from app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers.py rename to app/src/unity_sps_ogc_processes_api/models/fields_modifiers.py index 9b71705..34abada 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers.py +++ b/app/src/unity_sps_ogc_processes_api/models/fields_modifiers.py @@ -21,10 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - -from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( - FieldsModifiersProperties, -) +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import FieldsModifiersProperties try: from typing import Self diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py b/app/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py rename to app/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/format.py b/app/src/unity_sps_ogc_processes_api/models/format.py similarity index 99% rename from app-cp/src/unity_sps_ogc_processes_api/models/format.py rename to app/src/unity_sps_ogc_processes_api/models/format.py index c795faf..ebe480c 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/format.py +++ b/app/src/unity_sps_ogc_processes_api/models/format.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - from unity_sps_ogc_processes_api.models.format_schema import FormatSchema try: diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/format_schema.py b/app/src/unity_sps_ogc_processes_api/models/format_schema.py similarity index 94% rename from app-cp/src/unity_sps_ogc_processes_api/models/format_schema.py rename to app/src/unity_sps_ogc_processes_api/models/format_schema.py index f0f0067..adf0598 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/format_schema.py +++ b/app/src/unity_sps_ogc_processes_api/models/format_schema.py @@ -51,13 +51,9 @@ class FormatSchema(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature.py similarity index 89% rename from app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature.py rename to app/src/unity_sps_ogc_processes_api/models/geo_json_feature.py index ce8da2e..cd2e36a 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature.py @@ -20,19 +20,9 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import ( - BaseModel, - Field, - StrictFloat, - StrictInt, - StrictStr, - field_validator, -) +from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator from typing_extensions import Annotated - -from unity_sps_ogc_processes_api.models.geo_json_feature_geometry import ( - GeoJSONFeatureGeometry, -) +from unity_sps_ogc_processes_api.models.geo_json_feature_geometry import GeoJSONFeatureGeometry from unity_sps_ogc_processes_api.models.geo_json_feature_id import GeoJSONFeatureId try: @@ -50,9 +40,7 @@ class GeoJSONFeature(BaseModel): id: Optional[GeoJSONFeatureId] = None properties: Optional[Dict[str, Any]] geometry: GeoJSONFeatureGeometry - bbox: Optional[ - Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] - ] = None + bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None __properties: ClassVar[List[str]] = ["type", "id", "properties", "geometry", "bbox"] @field_validator("type") @@ -122,11 +110,7 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { "type": obj.get("type"), - "id": ( - GeoJSONFeatureId.from_dict(obj.get("id")) - if obj.get("id") is not None - else None - ), + "id": (GeoJSONFeatureId.from_dict(obj.get("id")) if obj.get("id") is not None else None), "properties": obj.get("properties"), "geometry": ( GeoJSONFeatureGeometry.from_dict(obj.get("geometry")) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py similarity index 87% rename from app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py rename to app/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py index 557cefd..68639ad 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py @@ -22,15 +22,10 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal - from unity_sps_ogc_processes_api.models.geo_json_line_string import GeoJSONLineString -from unity_sps_ogc_processes_api.models.geo_json_multi_line_string import ( - GeoJSONMultiLineString, -) +from unity_sps_ogc_processes_api.models.geo_json_multi_line_string import GeoJSONMultiLineString from unity_sps_ogc_processes_api.models.geo_json_multi_point import GeoJSONMultiPoint -from unity_sps_ogc_processes_api.models.geo_json_multi_polygon import ( - GeoJSONMultiPolygon, -) +from unity_sps_ogc_processes_api.models.geo_json_multi_polygon import GeoJSONMultiPolygon from unity_sps_ogc_processes_api.models.geo_json_point import GeoJSONPoint from unity_sps_ogc_processes_api.models.geo_json_polygon import GeoJSONPolygon @@ -93,13 +88,9 @@ class GeoJSONFeatureGeometry(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -111,44 +102,32 @@ def actual_instance_must_validate_oneof(cls, v): match = 0 # validate data type: GeoJSONPoint if not isinstance(v, GeoJSONPoint): - error_messages.append( - f"Error! Input type `{type(v)}` is not `GeoJSONPoint`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `GeoJSONPoint`") else: match += 1 # validate data type: GeoJSONLineString if not isinstance(v, GeoJSONLineString): - error_messages.append( - f"Error! Input type `{type(v)}` is not `GeoJSONLineString`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `GeoJSONLineString`") else: match += 1 # validate data type: GeoJSONPolygon if not isinstance(v, GeoJSONPolygon): - error_messages.append( - f"Error! Input type `{type(v)}` is not `GeoJSONPolygon`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `GeoJSONPolygon`") else: match += 1 # validate data type: GeoJSONMultiPoint if not isinstance(v, GeoJSONMultiPoint): - error_messages.append( - f"Error! Input type `{type(v)}` is not `GeoJSONMultiPoint`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `GeoJSONMultiPoint`") else: match += 1 # validate data type: GeoJSONMultiLineString if not isinstance(v, GeoJSONMultiLineString): - error_messages.append( - f"Error! Input type `{type(v)}` is not `GeoJSONMultiLineString`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `GeoJSONMultiLineString`") else: match += 1 # validate data type: GeoJSONMultiPolygon if not isinstance(v, GeoJSONMultiPolygon): - error_messages.append( - f"Error! Input type `{type(v)}` is not `GeoJSONMultiPolygon`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `GeoJSONMultiPolygon`") else: match += 1 if match > 1: diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py similarity index 92% rename from app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py rename to app/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py index 7062157..b1ece61 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py @@ -20,14 +20,7 @@ import re # noqa: F401 from typing import Dict, List, Optional, Union -from pydantic import ( - BaseModel, - StrictFloat, - StrictInt, - StrictStr, - ValidationError, - field_validator, -) +from pydantic import BaseModel, StrictFloat, StrictInt, StrictStr, ValidationError, field_validator from typing_extensions import Literal try: @@ -58,13 +51,9 @@ class GeoJSONFeatureId(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py similarity index 92% rename from app-cp/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py rename to app/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py index 5e38591..f0a7119 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py @@ -20,14 +20,7 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import ( - BaseModel, - Field, - StrictFloat, - StrictInt, - StrictStr, - field_validator, -) +from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator from typing_extensions import Annotated try: @@ -46,9 +39,7 @@ class GeoJSONLineString(BaseModel): List[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)]], Field(min_length=2), ] - bbox: Optional[ - Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] - ] = None + bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] @field_validator("type") diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py similarity index 92% rename from app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py rename to app/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py index 4e13cb8..cca0b2b 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py @@ -20,14 +20,7 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import ( - BaseModel, - Field, - StrictFloat, - StrictInt, - StrictStr, - field_validator, -) +from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator from typing_extensions import Annotated try: @@ -48,9 +41,7 @@ class GeoJSONMultiLineString(BaseModel): Field(min_length=2), ] ] - bbox: Optional[ - Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] - ] = None + bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] @field_validator("type") diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py similarity index 89% rename from app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py rename to app/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py index 8d64573..bea5843 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py @@ -20,14 +20,7 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import ( - BaseModel, - Field, - StrictFloat, - StrictInt, - StrictStr, - field_validator, -) +from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator from typing_extensions import Annotated try: @@ -42,12 +35,8 @@ class GeoJSONMultiPoint(BaseModel): """ # noqa: E501 type: StrictStr - coordinates: List[ - Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)] - ] - bbox: Optional[ - Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] - ] = None + coordinates: List[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)]] + bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] @field_validator("type") diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py similarity index 89% rename from app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py rename to app/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py index 56775ac..ebe5b46 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py @@ -20,14 +20,7 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import ( - BaseModel, - Field, - StrictFloat, - StrictInt, - StrictStr, - field_validator, -) +from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator from typing_extensions import Annotated try: @@ -45,16 +38,12 @@ class GeoJSONMultiPolygon(BaseModel): coordinates: List[ List[ Annotated[ - List[ - Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)] - ], + List[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)]], Field(min_length=4), ] ] ] - bbox: Optional[ - Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] - ] = None + bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] @field_validator("type") diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_point.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_point.py similarity index 92% rename from app-cp/src/unity_sps_ogc_processes_api/models/geo_json_point.py rename to app/src/unity_sps_ogc_processes_api/models/geo_json_point.py index c8eb97f..fa7869e 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_point.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_point.py @@ -20,14 +20,7 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import ( - BaseModel, - Field, - StrictFloat, - StrictInt, - StrictStr, - field_validator, -) +from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator from typing_extensions import Annotated try: @@ -43,9 +36,7 @@ class GeoJSONPoint(BaseModel): type: StrictStr coordinates: Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)] - bbox: Optional[ - Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] - ] = None + bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] @field_validator("type") diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py similarity index 92% rename from app-cp/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py rename to app/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py index c535824..5233ba1 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py @@ -20,14 +20,7 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import ( - BaseModel, - Field, - StrictFloat, - StrictInt, - StrictStr, - field_validator, -) +from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator from typing_extensions import Annotated try: @@ -48,9 +41,7 @@ class GeoJSONPolygon(BaseModel): Field(min_length=4), ] ] - bbox: Optional[ - Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] - ] = None + bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] @field_validator("type") diff --git a/app/src/unity_sps_ogc_processes_api/models/health_check.py b/app/src/unity_sps_ogc_processes_api/models/health_check.py new file mode 100644 index 0000000..7d00fff --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/models/health_check.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class HealthCheck(BaseModel): + status: str diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py similarity index 94% rename from app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py rename to app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py index 2bba538..d4cca9d 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py +++ b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py @@ -20,7 +20,6 @@ from typing import Any, Dict, List, Union from pydantic import RootModel, model_validator - from unity_sps_ogc_processes_api.models.bbox import Bbox from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.qualified_input_value import QualifiedInputValue @@ -40,9 +39,7 @@ def validate_type(cls, value): if "bbox" in value: return Bbox(**value) return value # Handle other dict cases - elif isinstance( - value, (Bbox, Link, QualifiedInputValue, bool, int, float, str) - ): + elif isinstance(value, (Bbox, Link, QualifiedInputValue, bool, int, float, str)): return value elif isinstance(value, list): return value diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py similarity index 86% rename from app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py rename to app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py index 0bf5526..08b49fb 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py +++ b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py @@ -22,14 +22,9 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal - -from unity_sps_ogc_processes_api.models.input_value_no_object1 import ( - InputValueNoObject1, -) +from unity_sps_ogc_processes_api.models.input_value_no_object1 import InputValueNoObject1 from unity_sps_ogc_processes_api.models.link import Link -from unity_sps_ogc_processes_api.models.qualified_input_value1 import ( - QualifiedInputValue1, -) +from unity_sps_ogc_processes_api.models.qualified_input_value1 import QualifiedInputValue1 try: from typing import Self @@ -54,12 +49,8 @@ class InlineOrRefData1(BaseModel): oneof_schema_2_validator: Optional[QualifiedInputValue1] = None # data type: Link oneof_schema_3_validator: Optional[Link] = None - actual_instance: Optional[ - Union[InputValueNoObject1, Link, QualifiedInputValue1] - ] = None - one_of_schemas: List[str] = Literal[ - "InputValueNoObject1", "Link", "QualifiedInputValue1" - ] + actual_instance: Optional[Union[InputValueNoObject1, Link, QualifiedInputValue1]] = None + one_of_schemas: List[str] = Literal["InputValueNoObject1", "Link", "QualifiedInputValue1"] model_config = { "validate_assignment": True, @@ -69,13 +60,9 @@ class InlineOrRefData1(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -87,16 +74,12 @@ def actual_instance_must_validate_oneof(cls, v): match = 0 # validate data type: InputValueNoObject1 if not isinstance(v, InputValueNoObject1): - error_messages.append( - f"Error! Input type `{type(v)}` is not `InputValueNoObject1`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `InputValueNoObject1`") else: match += 1 # validate data type: QualifiedInputValue1 if not isinstance(v, QualifiedInputValue1): - error_messages.append( - f"Error! Input type `{type(v)}` is not `QualifiedInputValue1`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `QualifiedInputValue1`") else: match += 1 # validate data type: Link diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py similarity index 86% rename from app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py rename to app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py index 7714621..d2bc4ab 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py @@ -22,7 +22,6 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal - from unity_sps_ogc_processes_api.models.link import Link try: @@ -48,12 +47,8 @@ class InlineOrRefDataWorkflows(BaseModel): oneof_schema_2_validator: Optional[QualifiedInputValueWorkflows] = None # data type: Link oneof_schema_3_validator: Optional[Link] = None - actual_instance: Optional[ - Union[InputValueNoObjectWorkflows, Link, QualifiedInputValueWorkflows] - ] = None - one_of_schemas: List[str] = Literal[ - "InputValueNoObjectWorkflows", "Link", "QualifiedInputValueWorkflows" - ] + actual_instance: Optional[Union[InputValueNoObjectWorkflows, Link, QualifiedInputValueWorkflows]] = None + one_of_schemas: List[str] = Literal["InputValueNoObjectWorkflows", "Link", "QualifiedInputValueWorkflows"] model_config = { "validate_assignment": True, @@ -63,13 +58,9 @@ class InlineOrRefDataWorkflows(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -81,16 +72,12 @@ def actual_instance_must_validate_oneof(cls, v): match = 0 # validate data type: InputValueNoObjectWorkflows if not isinstance(v, InputValueNoObjectWorkflows): - error_messages.append( - f"Error! Input type `{type(v)}` is not `InputValueNoObjectWorkflows`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `InputValueNoObjectWorkflows`") else: match += 1 # validate data type: QualifiedInputValueWorkflows if not isinstance(v, QualifiedInputValueWorkflows): - error_messages.append( - f"Error! Input type `{type(v)}` is not `QualifiedInputValueWorkflows`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `QualifiedInputValueWorkflows`") else: match += 1 # validate data type: Link @@ -186,12 +173,8 @@ def to_str(self) -> str: return pprint.pformat(self.model_dump()) -from unity_sps_ogc_processes_api.models.input_value_no_object_workflows import ( - InputValueNoObjectWorkflows, -) -from unity_sps_ogc_processes_api.models.qualified_input_value_workflows import ( - QualifiedInputValueWorkflows, -) +from unity_sps_ogc_processes_api.models.input_value_no_object_workflows import InputValueNoObjectWorkflows +from unity_sps_ogc_processes_api.models.qualified_input_value_workflows import QualifiedInputValueWorkflows # TODO: Rewrite to not use raise_errors InlineOrRefDataWorkflows.model_rebuild(raise_errors=False) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input.py b/app/src/unity_sps_ogc_processes_api/models/input.py similarity index 87% rename from app-cp/src/unity_sps_ogc_processes_api/models/input.py rename to app/src/unity_sps_ogc_processes_api/models/input.py index f0c3271..b619a99 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input.py +++ b/app/src/unity_sps_ogc_processes_api/models/input.py @@ -5,12 +5,9 @@ from typing import Any, Dict, List, Union from pydantic import RootModel, model_validator - from unity_sps_ogc_processes_api.models.bbox1 import Bbox1 from unity_sps_ogc_processes_api.models.link import Link -from unity_sps_ogc_processes_api.models.qualified_input_value1 import ( - QualifiedInputValue1, -) +from unity_sps_ogc_processes_api.models.qualified_input_value1 import QualifiedInputValue1 class Input(RootModel): @@ -23,9 +20,7 @@ class Input(RootModel): str, Link, QualifiedInputValue1, - List[ - Union[Bbox1, List[Any], bool, float, int, str, Link, QualifiedInputValue1] - ], + List[Union[Bbox1, List[Any], bool, float, int, str, Link, QualifiedInputValue1]], ] @model_validator(mode="before") @@ -40,9 +35,7 @@ def validate_type(cls, value): return Link(**value) elif isinstance(value, list): return [cls.validate_type(item) for item in value] - elif isinstance( - value, (bool, int, float, str, Bbox1, Link, QualifiedInputValue1) - ): + elif isinstance(value, (bool, int, float, str, Bbox1, Link, QualifiedInputValue1)): return value elif isinstance(value, List): return value diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_collection.py b/app/src/unity_sps_ogc_processes_api/models/input_collection.py similarity index 98% rename from app-cp/src/unity_sps_ogc_processes_api/models/input_collection.py rename to app/src/unity_sps_ogc_processes_api/models/input_collection.py index fa243d3..803ce6f 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_collection.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_collection.py @@ -21,10 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - -from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( - FieldsModifiersProperties, -) +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import FieldsModifiersProperties try: from typing import Self diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_description.py b/app/src/unity_sps_ogc_processes_api/models/input_description.py similarity index 99% rename from app-cp/src/unity_sps_ogc_processes_api/models/input_description.py rename to app/src/unity_sps_ogc_processes_api/models/input_description.py index 69e9b1b..233062f 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_description.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_description.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictInt, StrictStr, field_validator - from unity_sps_ogc_processes_api.models.input_description_all_of_max_occurs import ( InputDescriptionAllOfMaxOccurs, ) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py b/app/src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py similarity index 94% rename from app-cp/src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py rename to app/src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py index 20d2005..c58ca3b 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py @@ -51,13 +51,9 @@ class InputDescriptionAllOfMaxOccurs(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_parameterized.py b/app/src/unity_sps_ogc_processes_api/models/input_parameterized.py similarity index 98% rename from app-cp/src/unity_sps_ogc_processes_api/models/input_parameterized.py rename to app/src/unity_sps_ogc_processes_api/models/input_parameterized.py index 71b58f8..aa22d1a 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_parameterized.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_parameterized.py @@ -21,10 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - -from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( - FieldsModifiersProperties, -) +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import FieldsModifiersProperties try: from typing import Self diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_process.py b/app/src/unity_sps_ogc_processes_api/models/input_process.py similarity index 90% rename from app-cp/src/unity_sps_ogc_processes_api/models/input_process.py rename to app/src/unity_sps_ogc_processes_api/models/input_process.py index e80c881..a7f83bd 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_process.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_process.py @@ -21,10 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - -from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( - FieldsModifiersProperties, -) +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import FieldsModifiersProperties from unity_sps_ogc_processes_api.models.output_workflows1 import OutputWorkflows1 from unity_sps_ogc_processes_api.models.subscriber import Subscriber @@ -128,25 +125,17 @@ def from_dict(cls, obj: Dict) -> Self: { "process": obj.get("process"), "inputs": ( - dict( - (_k, InputWorkflows1.from_dict(_v)) - for _k, _v in obj.get("inputs").items() - ) + dict((_k, InputWorkflows1.from_dict(_v)) for _k, _v in obj.get("inputs").items()) if obj.get("inputs") is not None else None ), "outputs": ( - dict( - (_k, OutputWorkflows1.from_dict(_v)) - for _k, _v in obj.get("outputs").items() - ) + dict((_k, OutputWorkflows1.from_dict(_v)) for _k, _v in obj.get("outputs").items()) if obj.get("outputs") is not None else None ), "subscriber": ( - Subscriber.from_dict(obj.get("subscriber")) - if obj.get("subscriber") is not None - else None + Subscriber.from_dict(obj.get("subscriber")) if obj.get("subscriber") is not None else None ), "filter": obj.get("filter"), "properties": ( diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_value.py b/app/src/unity_sps_ogc_processes_api/models/input_value.py similarity index 91% rename from app-cp/src/unity_sps_ogc_processes_api/models/input_value.py rename to app/src/unity_sps_ogc_processes_api/models/input_value.py index aec7958..568a79a 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_value.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value.py @@ -22,7 +22,6 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal - from unity_sps_ogc_processes_api.models.input_value_no_object import InputValueNoObject try: @@ -56,13 +55,9 @@ class InputValue(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -73,9 +68,7 @@ def actual_instance_must_validate_anyof(cls, v): error_messages = [] # validate data type: InputValueNoObject if not isinstance(v, InputValueNoObject): - error_messages.append( - f"Error! Input type `{type(v)}` is not `InputValueNoObject`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `InputValueNoObject`") else: return v diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_value1.py b/app/src/unity_sps_ogc_processes_api/models/input_value1.py similarity index 91% rename from app-cp/src/unity_sps_ogc_processes_api/models/input_value1.py rename to app/src/unity_sps_ogc_processes_api/models/input_value1.py index e51aafd..7c070f4 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_value1.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value1.py @@ -22,10 +22,7 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal - -from unity_sps_ogc_processes_api.models.input_value_no_object1 import ( - InputValueNoObject1, -) +from unity_sps_ogc_processes_api.models.input_value_no_object1 import InputValueNoObject1 try: from typing import Self @@ -58,13 +55,9 @@ class InputValue1(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -75,9 +68,7 @@ def actual_instance_must_validate_anyof(cls, v): error_messages = [] # validate data type: InputValueNoObject1 if not isinstance(v, InputValueNoObject1): - error_messages.append( - f"Error! Input type `{type(v)}` is not `InputValueNoObject1`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `InputValueNoObject1`") else: return v diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object.py b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object.py similarity index 95% rename from app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object.py rename to app/src/unity_sps_ogc_processes_api/models/input_value_no_object.py index fc969fd..c11dfde 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object.py @@ -31,7 +31,6 @@ field_validator, ) from typing_extensions import Literal - from unity_sps_ogc_processes_api.models.bbox import Bbox try: @@ -69,9 +68,7 @@ class InputValueNoObject(BaseModel): # data type: Bbox oneof_schema_7_validator: Optional[Bbox] = None actual_instance: Optional[Union[Bbox, List[object], bool, float, int, str]] = None - one_of_schemas: List[str] = Literal[ - "Bbox", "List[object]", "bool", "float", "int", "str" - ] + one_of_schemas: List[str] = Literal["Bbox", "List[object]", "bool", "float", "int", "str"] model_config = { "validate_assignment": True, @@ -81,13 +78,9 @@ class InputValueNoObject(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py similarity index 95% rename from app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py rename to app/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py index 9dd2681..5de380e 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py @@ -31,7 +31,6 @@ field_validator, ) from typing_extensions import Literal - from unity_sps_ogc_processes_api.models.bbox1 import Bbox1 try: @@ -69,9 +68,7 @@ class InputValueNoObject1(BaseModel): # data type: Bbox1 oneof_schema_7_validator: Optional[Bbox1] = None actual_instance: Optional[Union[Bbox1, List[object], bool, float, int, str]] = None - one_of_schemas: List[str] = Literal[ - "Bbox1", "List[object]", "bool", "float", "int", "str" - ] + one_of_schemas: List[str] = Literal["Bbox1", "List[object]", "bool", "float", "int", "str"] model_config = { "validate_assignment": True, @@ -81,13 +78,9 @@ class InputValueNoObject1(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py similarity index 94% rename from app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py rename to app/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py index fc49e7e..2bff507 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py @@ -31,7 +31,6 @@ field_validator, ) from typing_extensions import Literal - from unity_sps_ogc_processes_api.models.bbox1 import Bbox1 from unity_sps_ogc_processes_api.models.input_collection import InputCollection from unity_sps_ogc_processes_api.models.input_parameterized import InputParameterized @@ -112,13 +111,9 @@ class InputValueNoObjectWorkflows(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -171,23 +166,17 @@ def actual_instance_must_validate_oneof(cls, v): match += 1 # validate data type: InputCollection if not isinstance(v, InputCollection): - error_messages.append( - f"Error! Input type `{type(v)}` is not `InputCollection`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `InputCollection`") else: match += 1 # validate data type: InputProcess if not isinstance(v, InputProcess): - error_messages.append( - f"Error! Input type `{type(v)}` is not `InputProcess`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `InputProcess`") else: match += 1 # validate data type: InputParameterized if not isinstance(v, InputParameterized): - error_messages.append( - f"Error! Input type `{type(v)}` is not `InputParameterized`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `InputParameterized`") else: match += 1 if match > 1: diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_value_workflows.py b/app/src/unity_sps_ogc_processes_api/models/input_value_workflows.py similarity index 91% rename from app-cp/src/unity_sps_ogc_processes_api/models/input_value_workflows.py rename to app/src/unity_sps_ogc_processes_api/models/input_value_workflows.py index 6d18d3a..0f64ec0 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_value_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value_workflows.py @@ -51,13 +51,9 @@ class InputValueWorkflows(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -69,9 +65,7 @@ def actual_instance_must_validate_oneof(cls, v): match = 0 # validate data type: InputValueNoObjectWorkflows if not isinstance(v, InputValueNoObjectWorkflows): - error_messages.append( - f"Error! Input type `{type(v)}` is not `InputValueNoObjectWorkflows`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `InputValueNoObjectWorkflows`") else: match += 1 # validate data type: object @@ -165,9 +159,7 @@ def to_str(self) -> str: return pprint.pformat(self.model_dump()) -from unity_sps_ogc_processes_api.models.input_value_no_object_workflows import ( - InputValueNoObjectWorkflows, -) +from unity_sps_ogc_processes_api.models.input_value_no_object_workflows import InputValueNoObjectWorkflows # TODO: Rewrite to not use raise_errors InputValueWorkflows.model_rebuild(raise_errors=False) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_workflows.py b/app/src/unity_sps_ogc_processes_api/models/input_workflows.py similarity index 98% rename from app-cp/src/unity_sps_ogc_processes_api/models/input_workflows.py rename to app/src/unity_sps_ogc_processes_api/models/input_workflows.py index 65bb11b..a5052e0 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_workflows.py @@ -20,15 +20,12 @@ from typing import Any, Dict, List, Union from pydantic import RootModel, ValidationError, model_validator - from unity_sps_ogc_processes_api.models.bbox1 import Bbox1 from unity_sps_ogc_processes_api.models.input_collection import InputCollection from unity_sps_ogc_processes_api.models.input_parameterized import InputParameterized from unity_sps_ogc_processes_api.models.input_process import InputProcess from unity_sps_ogc_processes_api.models.link import Link -from unity_sps_ogc_processes_api.models.qualified_input_value_workflows import ( - QualifiedInputValueWorkflows, -) +from unity_sps_ogc_processes_api.models.qualified_input_value_workflows import QualifiedInputValueWorkflows class InputWorkflows(RootModel): diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/input_workflows1.py b/app/src/unity_sps_ogc_processes_api/models/input_workflows1.py similarity index 88% rename from app-cp/src/unity_sps_ogc_processes_api/models/input_workflows1.py rename to app/src/unity_sps_ogc_processes_api/models/input_workflows1.py index 6dd9a1e..c58823c 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/input_workflows1.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_workflows1.py @@ -43,12 +43,8 @@ class InputWorkflows1(BaseModel): oneof_schema_1_validator: Optional[InlineOrRefDataWorkflows] = None # data type: List[InlineOrRefDataWorkflows] oneof_schema_2_validator: Optional[List[InlineOrRefDataWorkflows]] = None - actual_instance: Optional[ - Union[InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]] - ] = None - one_of_schemas: List[str] = Literal[ - "InlineOrRefDataWorkflows", "List[InlineOrRefDataWorkflows]" - ] + actual_instance: Optional[Union[InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]]] = None + one_of_schemas: List[str] = Literal["InlineOrRefDataWorkflows", "List[InlineOrRefDataWorkflows]"] model_config = { "validate_assignment": True, @@ -58,13 +54,9 @@ class InputWorkflows1(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -76,9 +68,7 @@ def actual_instance_must_validate_oneof(cls, v): match = 0 # validate data type: InlineOrRefDataWorkflows if not isinstance(v, InlineOrRefDataWorkflows): - error_messages.append( - f"Error! Input type `{type(v)}` is not `InlineOrRefDataWorkflows`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `InlineOrRefDataWorkflows`") else: match += 1 # validate data type: List[InlineOrRefDataWorkflows] @@ -172,9 +162,7 @@ def to_str(self) -> str: return pprint.pformat(self.model_dump()) -from unity_sps_ogc_processes_api.models.inline_or_ref_data_workflows import ( - InlineOrRefDataWorkflows, -) +from unity_sps_ogc_processes_api.models.inline_or_ref_data_workflows import InlineOrRefDataWorkflows # TODO: Rewrite to not use raise_errors InputWorkflows1.model_rebuild(raise_errors=False) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/job_control_options.py b/app/src/unity_sps_ogc_processes_api/models/job_control_options.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/job_control_options.py rename to app/src/unity_sps_ogc_processes_api/models/job_control_options.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/job_list.py b/app/src/unity_sps_ogc_processes_api/models/job_list.py similarity index 99% rename from app-cp/src/unity_sps_ogc_processes_api/models/job_list.py rename to app/src/unity_sps_ogc_processes_api/models/job_list.py index 9fe2f2f..5dbffae 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/job_list.py +++ b/app/src/unity_sps_ogc_processes_api/models/job_list.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List from pydantic import BaseModel - from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.status_info import StatusInfo diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/landing_page.py b/app/src/unity_sps_ogc_processes_api/models/landing_page.py similarity index 99% rename from app-cp/src/unity_sps_ogc_processes_api/models/landing_page.py rename to app/src/unity_sps_ogc_processes_api/models/landing_page.py index 2fffd7f..3b3805c 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/landing_page.py +++ b/app/src/unity_sps_ogc_processes_api/models/landing_page.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - from unity_sps_ogc_processes_api.models.link import Link try: diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/link.py b/app/src/unity_sps_ogc_processes_api/models/link.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/link.py rename to app/src/unity_sps_ogc_processes_api/models/link.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/metadata.py b/app/src/unity_sps_ogc_processes_api/models/metadata.py similarity index 99% rename from app-cp/src/unity_sps_ogc_processes_api/models/metadata.py rename to app/src/unity_sps_ogc_processes_api/models/metadata.py index 963f702..de30ee9 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/metadata.py +++ b/app/src/unity_sps_ogc_processes_api/models/metadata.py @@ -4,7 +4,6 @@ from typing import Any, Dict, Union from pydantic import RootModel, ValidationError, model_validator - from unity_sps_ogc_processes_api.models.metadata_one_of import MetadataOneOf from unity_sps_ogc_processes_api.models.metadata_one_of1 import MetadataOneOf1 diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of.py b/app/src/unity_sps_ogc_processes_api/models/metadata_one_of.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of.py rename to app/src/unity_sps_ogc_processes_api/models/metadata_one_of.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py b/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py similarity index 96% rename from app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py rename to app/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py index cc3eeb3..d54cd8f 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py +++ b/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py @@ -21,10 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, StrictStr - -from unity_sps_ogc_processes_api.models.metadata_one_of1_value import ( - MetadataOneOf1Value, -) +from unity_sps_ogc_processes_api.models.metadata_one_of1_value import MetadataOneOf1Value try: from typing import Self @@ -98,9 +95,7 @@ def from_dict(cls, obj: Dict) -> Self: "title": obj.get("title"), "lang": obj.get("lang"), "value": ( - MetadataOneOf1Value.from_dict(obj.get("value")) - if obj.get("value") is not None - else None + MetadataOneOf1Value.from_dict(obj.get("value")) if obj.get("value") is not None else None ), } ) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py b/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py similarity index 94% rename from app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py rename to app/src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py index 2930109..20ee6df 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py +++ b/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py @@ -51,13 +51,9 @@ class MetadataOneOf1Value(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/model_schema.py b/app/src/unity_sps_ogc_processes_api/models/model_schema.py similarity index 99% rename from app-cp/src/unity_sps_ogc_processes_api/models/model_schema.py rename to app/src/unity_sps_ogc_processes_api/models/model_schema.py index a9e7f42..b8834e8 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/model_schema.py +++ b/app/src/unity_sps_ogc_processes_api/models/model_schema.py @@ -20,7 +20,6 @@ from typing import Any, Dict, Union from pydantic import RootModel, ValidationError, model_validator - from unity_sps_ogc_processes_api.models.reference import Reference from unity_sps_ogc_processes_api.models.schema_one_of import SchemaOneOf diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg.py b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg.py similarity index 98% rename from app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg.py rename to app/src/unity_sps_ogc_processes_api/models/ogcapppkg.py index 5c5d2c3..f1265e1 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg.py +++ b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg.py @@ -20,10 +20,7 @@ from typing import Any, ClassVar, Dict, List from pydantic import BaseModel, Field - -from unity_sps_ogc_processes_api.models.ogcapppkg_execution_unit import ( - OgcapppkgExecutionUnit, -) +from unity_sps_ogc_processes_api.models.ogcapppkg_execution_unit import OgcapppkgExecutionUnit from unity_sps_ogc_processes_api.models.process import Process try: diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py similarity index 91% rename from app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py rename to app/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py index eea9993..4d34690 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py +++ b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py @@ -22,7 +22,6 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal - from unity_sps_ogc_processes_api.models.execution_unit import ExecutionUnit from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.qualified_input_value import QualifiedInputValue @@ -57,13 +56,9 @@ class OgcapppkgArrayInner(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -75,9 +70,7 @@ def actual_instance_must_validate_oneof(cls, v): match = 0 # validate data type: ExecutionUnit if not isinstance(v, ExecutionUnit): - error_messages.append( - f"Error! Input type `{type(v)}` is not `ExecutionUnit`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `ExecutionUnit`") else: match += 1 # validate data type: Link @@ -87,9 +80,7 @@ def actual_instance_must_validate_oneof(cls, v): match += 1 # validate data type: QualifiedInputValue if not isinstance(v, QualifiedInputValue): - error_messages.append( - f"Error! Input type `{type(v)}` is not `QualifiedInputValue`" - ) + error_messages.append(f"Error! Input type `{type(v)}` is not `QualifiedInputValue`") else: match += 1 if match > 1: diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py similarity index 99% rename from app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py rename to app/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py index 2448633..122bd01 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py +++ b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py @@ -20,7 +20,6 @@ from typing import Any, Dict, List, Union from pydantic import RootModel, ValidationError, model_validator - from unity_sps_ogc_processes_api.models.execution_unit import ExecutionUnit from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.qualified_input_value import QualifiedInputValue diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/output.py b/app/src/unity_sps_ogc_processes_api/models/output.py similarity index 92% rename from app-cp/src/unity_sps_ogc_processes_api/models/output.py rename to app/src/unity_sps_ogc_processes_api/models/output.py index bfc5305..735b4df 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/output.py +++ b/app/src/unity_sps_ogc_processes_api/models/output.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel - from unity_sps_ogc_processes_api.models.format import Format try: @@ -88,12 +87,6 @@ def from_dict(cls, obj: Dict) -> Self: return cls.model_validate(obj) _obj = cls.model_validate( - { - "format": ( - Format.from_dict(obj.get("format")) - if obj.get("format") is not None - else None - ) - } + {"format": (Format.from_dict(obj.get("format")) if obj.get("format") is not None else None)} ) return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/output_description.py b/app/src/unity_sps_ogc_processes_api/models/output_description.py similarity index 99% rename from app-cp/src/unity_sps_ogc_processes_api/models/output_description.py rename to app/src/unity_sps_ogc_processes_api/models/output_description.py index 4b81393..5e1e5d8 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/output_description.py +++ b/app/src/unity_sps_ogc_processes_api/models/output_description.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - from unity_sps_ogc_processes_api.models.metadata import Metadata from unity_sps_ogc_processes_api.models.model_schema import ModelSchema diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/output_workflows.py b/app/src/unity_sps_ogc_processes_api/models/output_workflows.py similarity index 93% rename from app-cp/src/unity_sps_ogc_processes_api/models/output_workflows.py rename to app/src/unity_sps_ogc_processes_api/models/output_workflows.py index 7431654..a0038b6 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/output_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/output_workflows.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - from unity_sps_ogc_processes_api.models.format import Format try: @@ -90,11 +89,7 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { - "format": ( - Format.from_dict(obj.get("format")) - if obj.get("format") is not None - else None - ), + "format": (Format.from_dict(obj.get("format")) if obj.get("format") is not None else None), "$output": obj.get("$output"), } ) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/output_workflows1.py b/app/src/unity_sps_ogc_processes_api/models/output_workflows1.py similarity index 93% rename from app-cp/src/unity_sps_ogc_processes_api/models/output_workflows1.py rename to app/src/unity_sps_ogc_processes_api/models/output_workflows1.py index 831e686..dccfcaf 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/output_workflows1.py +++ b/app/src/unity_sps_ogc_processes_api/models/output_workflows1.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - from unity_sps_ogc_processes_api.models.format import Format try: @@ -90,11 +89,7 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { - "format": ( - Format.from_dict(obj.get("format")) - if obj.get("format") is not None - else None - ), + "format": (Format.from_dict(obj.get("format")) if obj.get("format") is not None else None), "$output": obj.get("$output"), } ) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/process.py b/app/src/unity_sps_ogc_processes_api/models/process.py similarity index 93% rename from app-cp/src/unity_sps_ogc_processes_api/models/process.py rename to app/src/unity_sps_ogc_processes_api/models/process.py index af787f9..d6b4b8b 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/process.py +++ b/app/src/unity_sps_ogc_processes_api/models/process.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - from unity_sps_ogc_processes_api.models.input_description import InputDescription from unity_sps_ogc_processes_api.models.job_control_options import JobControlOptions from unity_sps_ogc_processes_api.models.link import Link @@ -45,9 +44,7 @@ class Process(BaseModel): metadata: Optional[List[Metadata]] = None id: StrictStr version: StrictStr - job_control_options: Optional[List[JobControlOptions]] = Field( - default=None, alias="jobControlOptions" - ) + job_control_options: Optional[List[JobControlOptions]] = Field(default=None, alias="jobControlOptions") links: Optional[List[Link]] = None inputs: Optional[Dict[str, InputDescription]] = None outputs: Optional[Dict[str, OutputDescription]] = None @@ -157,18 +154,12 @@ def from_dict(cls, obj: Dict) -> Self: else None ), "inputs": ( - dict( - (_k, InputDescription.from_dict(_v)) - for _k, _v in obj.get("inputs").items() - ) + dict((_k, InputDescription.from_dict(_v)) for _k, _v in obj.get("inputs").items()) if obj.get("inputs") is not None else None ), "outputs": ( - dict( - (_k, OutputDescription.from_dict(_v)) - for _k, _v in obj.get("outputs").items() - ) + dict((_k, OutputDescription.from_dict(_v)) for _k, _v in obj.get("outputs").items()) if obj.get("outputs") is not None else None ), diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/process_list.py b/app/src/unity_sps_ogc_processes_api/models/process_list.py similarity index 99% rename from app-cp/src/unity_sps_ogc_processes_api/models/process_list.py rename to app/src/unity_sps_ogc_processes_api/models/process_list.py index d7ac593..6657aa8 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/process_list.py +++ b/app/src/unity_sps_ogc_processes_api/models/process_list.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List from pydantic import BaseModel - from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.process_summary import ProcessSummary diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/process_summary.py b/app/src/unity_sps_ogc_processes_api/models/process_summary.py similarity index 98% rename from app-cp/src/unity_sps_ogc_processes_api/models/process_summary.py rename to app/src/unity_sps_ogc_processes_api/models/process_summary.py index d68436a..9d784d7 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/process_summary.py +++ b/app/src/unity_sps_ogc_processes_api/models/process_summary.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - from unity_sps_ogc_processes_api.models.job_control_options import JobControlOptions from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.metadata import Metadata @@ -43,9 +42,7 @@ class ProcessSummary(BaseModel): metadata: Optional[List[Metadata]] = None id: StrictStr version: StrictStr - job_control_options: Optional[List[JobControlOptions]] = Field( - default=None, alias="jobControlOptions" - ) + job_control_options: Optional[List[JobControlOptions]] = Field(default=None, alias="jobControlOptions") links: Optional[List[Link]] = None __properties: ClassVar[List[str]] = [ "title", diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/processes_list.py b/app/src/unity_sps_ogc_processes_api/models/processes_list.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/processes_list.py rename to app/src/unity_sps_ogc_processes_api/models/processes_list.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value.py b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value.py similarity index 99% rename from app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value.py rename to app/src/unity_sps_ogc_processes_api/models/qualified_input_value.py index d25f160..284ba54 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value.py +++ b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - from unity_sps_ogc_processes_api.models.format_schema import FormatSchema from unity_sps_ogc_processes_api.models.input_value import InputValue diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py similarity index 99% rename from app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py rename to app/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py index d82bd76..063a825 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py +++ b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - from unity_sps_ogc_processes_api.models.format_schema import FormatSchema from unity_sps_ogc_processes_api.models.input_value1 import InputValue1 diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py similarity index 98% rename from app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py rename to app/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py index 07feebd..9308292 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py @@ -21,10 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr - -from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( - FieldsModifiersProperties, -) +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import FieldsModifiersProperties from unity_sps_ogc_processes_api.models.format_schema import FormatSchema try: diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/reference.py b/app/src/unity_sps_ogc_processes_api/models/reference.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/reference.py rename to app/src/unity_sps_ogc_processes_api/models/reference.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/schema1.py b/app/src/unity_sps_ogc_processes_api/models/schema1.py similarity index 94% rename from app-cp/src/unity_sps_ogc_processes_api/models/schema1.py rename to app/src/unity_sps_ogc_processes_api/models/schema1.py index b31f8f1..d225f46 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/schema1.py +++ b/app/src/unity_sps_ogc_processes_api/models/schema1.py @@ -22,7 +22,6 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal - from unity_sps_ogc_processes_api.models.reference import Reference try: @@ -53,13 +52,9 @@ class Schema1(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/schema_one_of.py b/app/src/unity_sps_ogc_processes_api/models/schema_one_of.py similarity index 77% rename from app-cp/src/unity_sps_ogc_processes_api/models/schema_one_of.py rename to app/src/unity_sps_ogc_processes_api/models/schema_one_of.py index 7ded79c..149bc99 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/schema_one_of.py +++ b/app/src/unity_sps_ogc_processes_api/models/schema_one_of.py @@ -20,15 +20,7 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import ( - BaseModel, - Field, - StrictBool, - StrictFloat, - StrictInt, - StrictStr, - field_validator, -) +from pydantic import BaseModel, Field, StrictBool, StrictFloat, StrictInt, StrictStr, field_validator from typing_extensions import Annotated try: @@ -50,26 +42,14 @@ class SchemaOneOf(BaseModel): ] ] = Field(default=None, alias="multipleOf") maximum: Optional[Union[StrictFloat, StrictInt]] = None - exclusive_maximum: Optional[StrictBool] = Field( - default=False, alias="exclusiveMaximum" - ) + exclusive_maximum: Optional[StrictBool] = Field(default=False, alias="exclusiveMaximum") minimum: Optional[Union[StrictFloat, StrictInt]] = None - exclusive_minimum: Optional[StrictBool] = Field( - default=False, alias="exclusiveMinimum" - ) - max_length: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( - default=None, alias="maxLength" - ) - min_length: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( - default=0, alias="minLength" - ) + exclusive_minimum: Optional[StrictBool] = Field(default=False, alias="exclusiveMinimum") + max_length: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field(default=None, alias="maxLength") + min_length: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field(default=0, alias="minLength") pattern: Optional[StrictStr] = None - max_items: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( - default=None, alias="maxItems" - ) - min_items: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( - default=0, alias="minItems" - ) + max_items: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field(default=None, alias="maxItems") + min_items: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field(default=0, alias="minItems") unique_items: Optional[StrictBool] = Field(default=False, alias="uniqueItems") max_properties: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( default=None, alias="maxProperties" @@ -97,9 +77,7 @@ class SchemaOneOf(BaseModel): write_only: Optional[StrictBool] = Field(default=False, alias="writeOnly") example: Optional[Dict[str, Any]] = None deprecated: Optional[StrictBool] = False - content_media_type: Optional[StrictStr] = Field( - default=None, alias="contentMediaType" - ) + content_media_type: Optional[StrictStr] = Field(default=None, alias="contentMediaType") content_encoding: Optional[StrictStr] = Field(default=None, alias="contentEncoding") content_schema: Optional[StrictStr] = Field(default=None, alias="contentSchema") __properties: ClassVar[List[str]] = [ @@ -241,44 +219,24 @@ def from_dict(cls, obj: Dict) -> Self: "multipleOf": obj.get("multipleOf"), "maximum": obj.get("maximum"), "exclusiveMaximum": ( - obj.get("exclusiveMaximum") - if obj.get("exclusiveMaximum") is not None - else False + obj.get("exclusiveMaximum") if obj.get("exclusiveMaximum") is not None else False ), "minimum": obj.get("minimum"), "exclusiveMinimum": ( - obj.get("exclusiveMinimum") - if obj.get("exclusiveMinimum") is not None - else False + obj.get("exclusiveMinimum") if obj.get("exclusiveMinimum") is not None else False ), "maxLength": obj.get("maxLength"), - "minLength": ( - obj.get("minLength") if obj.get("minLength") is not None else 0 - ), + "minLength": (obj.get("minLength") if obj.get("minLength") is not None else 0), "pattern": obj.get("pattern"), "maxItems": obj.get("maxItems"), - "minItems": ( - obj.get("minItems") if obj.get("minItems") is not None else 0 - ), - "uniqueItems": ( - obj.get("uniqueItems") - if obj.get("uniqueItems") is not None - else False - ), + "minItems": (obj.get("minItems") if obj.get("minItems") is not None else 0), + "uniqueItems": (obj.get("uniqueItems") if obj.get("uniqueItems") is not None else False), "maxProperties": obj.get("maxProperties"), - "minProperties": ( - obj.get("minProperties") - if obj.get("minProperties") is not None - else 0 - ), + "minProperties": (obj.get("minProperties") if obj.get("minProperties") is not None else 0), "required": obj.get("required"), "enum": obj.get("enum"), "type": obj.get("type"), - "not": ( - Schema1.from_dict(obj.get("not")) - if obj.get("not") is not None - else None - ), + "not": (Schema1.from_dict(obj.get("not")) if obj.get("not") is not None else None), "allOf": ( [Schema1.from_dict(_item) for _item in obj.get("allOf")] if obj.get("allOf") is not None @@ -294,44 +252,25 @@ def from_dict(cls, obj: Dict) -> Self: if obj.get("anyOf") is not None else None ), - "items": ( - Schema1.from_dict(obj.get("items")) - if obj.get("items") is not None - else None - ), + "items": (Schema1.from_dict(obj.get("items")) if obj.get("items") is not None else None), "properties": ( - dict( - (_k, Schema1.from_dict(_v)) - for _k, _v in obj.get("properties").items() - ) + dict((_k, Schema1.from_dict(_v)) for _k, _v in obj.get("properties").items()) if obj.get("properties") is not None else None ), "additionalProperties": ( - SchemaOneOfAdditionalProperties.from_dict( - obj.get("additionalProperties") - ) + SchemaOneOfAdditionalProperties.from_dict(obj.get("additionalProperties")) if obj.get("additionalProperties") is not None else None ), "description": obj.get("description"), "format": obj.get("format"), "default": obj.get("default"), - "nullable": ( - obj.get("nullable") if obj.get("nullable") is not None else False - ), - "readOnly": ( - obj.get("readOnly") if obj.get("readOnly") is not None else False - ), - "writeOnly": ( - obj.get("writeOnly") if obj.get("writeOnly") is not None else False - ), + "nullable": (obj.get("nullable") if obj.get("nullable") is not None else False), + "readOnly": (obj.get("readOnly") if obj.get("readOnly") is not None else False), + "writeOnly": (obj.get("writeOnly") if obj.get("writeOnly") is not None else False), "example": obj.get("example"), - "deprecated": ( - obj.get("deprecated") - if obj.get("deprecated") is not None - else False - ), + "deprecated": (obj.get("deprecated") if obj.get("deprecated") is not None else False), "contentMediaType": obj.get("contentMediaType"), "contentEncoding": obj.get("contentEncoding"), "contentSchema": obj.get("contentSchema"), diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py b/app/src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py similarity index 94% rename from app-cp/src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py rename to app/src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py index 19a6381..3ae7829 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py +++ b/app/src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py @@ -51,13 +51,9 @@ class SchemaOneOfAdditionalProperties(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError( - "If a position argument is used, only 1 is allowed to set `actual_instance`" - ) + raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") if kwargs: - raise ValueError( - "If a position argument is used, keyword arguments cannot be used." - ) + raise ValueError("If a position argument is used, keyword arguments cannot be used.") super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/static_indicator.py b/app/src/unity_sps_ogc_processes_api/models/static_indicator.py similarity index 95% rename from app-cp/src/unity_sps_ogc_processes_api/models/static_indicator.py rename to app/src/unity_sps_ogc_processes_api/models/static_indicator.py index 24e3b14..de77c55 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/static_indicator.py +++ b/app/src/unity_sps_ogc_processes_api/models/static_indicator.py @@ -21,7 +21,6 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictBool, StrictStr - from unity_sps_ogc_processes_api.models.job_control_options import JobControlOptions from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.metadata import Metadata @@ -43,9 +42,7 @@ class StaticIndicator(BaseModel): metadata: Optional[List[Metadata]] = None id: StrictStr version: StrictStr - job_control_options: Optional[List[JobControlOptions]] = Field( - default=None, alias="jobControlOptions" - ) + job_control_options: Optional[List[JobControlOptions]] = Field(default=None, alias="jobControlOptions") links: Optional[List[Link]] = None mutable: Optional[StrictBool] = True __properties: ClassVar[List[str]] = [ @@ -138,9 +135,7 @@ def from_dict(cls, obj: Dict) -> Self: if obj.get("links") is not None else None ), - "mutable": ( - obj.get("mutable") if obj.get("mutable") is not None else True - ), + "mutable": (obj.get("mutable") if obj.get("mutable") is not None else True), } ) return _obj diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/status_code.py b/app/src/unity_sps_ogc_processes_api/models/status_code.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/status_code.py rename to app/src/unity_sps_ogc_processes_api/models/status_code.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/status_info.py b/app/src/unity_sps_ogc_processes_api/models/status_info.py similarity index 96% rename from app-cp/src/unity_sps_ogc_processes_api/models/status_info.py rename to app/src/unity_sps_ogc_processes_api/models/status_info.py index 212d05c..c662a02 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/models/status_info.py +++ b/app/src/unity_sps_ogc_processes_api/models/status_info.py @@ -23,7 +23,6 @@ from pydantic import BaseModel, Field, StrictStr, field_validator from typing_extensions import Annotated - from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.status_code import StatusCode @@ -137,9 +136,7 @@ def from_dict(cls, obj: Dict) -> Self: "status": obj.get("status"), "message": obj.get("message"), "exception": ( - Exception.from_dict(obj.get("exception")) - if obj.get("exception") is not None - else None + Exception.from_dict(obj.get("exception")) if obj.get("exception") is not None else None ), "created": obj.get("created"), "started": obj.get("started"), diff --git a/app-cp/src/unity_sps_ogc_processes_api/models/subscriber.py b/app/src/unity_sps_ogc_processes_api/models/subscriber.py similarity index 100% rename from app-cp/src/unity_sps_ogc_processes_api/models/subscriber.py rename to app/src/unity_sps_ogc_processes_api/models/subscriber.py diff --git a/app-cp/src/unity_sps_ogc_processes_api/security_api.py b/app/src/unity_sps_ogc_processes_api/security_api.py similarity index 77% rename from app-cp/src/unity_sps_ogc_processes_api/security_api.py rename to app/src/unity_sps_ogc_processes_api/security_api.py index dfc6b32..a3c9b25 100644 --- a/app-cp/src/unity_sps_ogc_processes_api/security_api.py +++ b/app/src/unity_sps_ogc_processes_api/security_api.py @@ -13,8 +13,4 @@ OAuth2PasswordBearer, SecurityScopes, ) -from fastapi.security.api_key import ( # noqa: F401 - APIKeyCookie, - APIKeyHeader, - APIKeyQuery, -) +from fastapi.security.api_key import APIKeyCookie, APIKeyHeader, APIKeyQuery # noqa: F401 diff --git a/app-cp/tests/conftest.py b/app/tests/conftest.py similarity index 96% rename from app-cp/tests/conftest.py rename to app/tests/conftest.py index 6b98e63..5e7c99d 100644 --- a/app-cp/tests/conftest.py +++ b/app/tests/conftest.py @@ -8,18 +8,13 @@ # from fastapi.encoders import jsonable_encoder from fastapi.testclient import TestClient -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker -from sqlalchemy.pool import StaticPool - from openapi_server.database import Base from openapi_server.database.models import Process from openapi_server.utils.redis import RedisLock -from unity_sps_ogc_processes_api.dependencies import ( - get_db, - get_redis_locking_client, - get_settings, -) +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.pool import StaticPool +from unity_sps_ogc_processes_api.dependencies import get_db, get_redis_locking_client, get_settings from unity_sps_ogc_processes_api.main import app settings = get_settings() @@ -61,18 +56,14 @@ def fake_filesystem(fs_session, test_directory): # pylint:disable=invalid-name """Variable name 'fs' causes a pylint warning. Provide a longer name acceptable to pylint for use in tests. """ - fs_session.add_real_directory( - os.path.join(test_directory, "..", "..", "process_descriptions") - ) + fs_session.add_real_directory(os.path.join(test_directory, "..", "..", "process_descriptions")) fs_session.add_real_directory(os.path.join(test_directory), "test_data") fs_session.create_dir(settings.DAG_CATALOG_DIRECTORY) fs_session.create_file( os.path.join(settings.DAG_CATALOG_DIRECTORY, "cwltool_help_dag.py"), contents="test", ) - fs_session.create_file( - os.path.join(settings.DAG_CATALOG_DIRECTORY, "EchoProcess.py"), contents="test" - ) + fs_session.create_file(os.path.join(settings.DAG_CATALOG_DIRECTORY, "EchoProcess.py"), contents="test") fs_session.create_dir(settings.DEPLOYED_DAGS_DIRECTORY) yield fs_session @@ -381,9 +372,7 @@ def mock_delete_existing_dag_dagrun(requests_mock): @pytest.fixture(scope="function", autouse=True) def mock_get_existing_running_dag_dagrun_tasks(requests_mock): return requests_mock.get( - re.compile( - f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)/taskInstances$" - ), + re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)/taskInstances$"), json={ "task_instances": [ { @@ -450,9 +439,7 @@ def mock_get_existing_running_dag_dagrun_tasks(requests_mock): @pytest.fixture(scope="function", autouse=True) def mock_patch_existing_running_dag_dagrun_task(requests_mock): return requests_mock.patch( - re.compile( - f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)/taskInstances/([^/]*)$" - ), + re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)/taskInstances/([^/]*)$"), json={ "task_id": "string", "dag_id": "string", @@ -464,9 +451,7 @@ def mock_patch_existing_running_dag_dagrun_task(requests_mock): @pytest.fixture(scope="function") def deploy_process(test_directory, client): - data_filename = os.path.join( - test_directory, "..", "process_descriptions", "cwltool_help_dag.json" - ) + data_filename = os.path.join(test_directory, "..", "process_descriptions", "cwltool_help_dag.json") f = open(data_filename) process_json = json.load(f) process = Process.model_validate(process_json) diff --git a/app/tests/echoProcess.json b/app/tests/echoProcess.json new file mode 100644 index 0000000..9d2405a --- /dev/null +++ b/app/tests/echoProcess.json @@ -0,0 +1,73 @@ +{ + "executionUnit": { + "additional_properties": {}, + "config": { + "additional_properties": {} + }, + "deployment": "cloud", + "image": "example/image:latest", + "type": "docker" + }, + "processDescription": { + "description": "This process performs an example task.", + "id": "EchoProcess", + "inputs": { + "input1": { + "description": "Description of Input 1", + "maxOccurs": 1, + "minOccurs": 1, + "schema": { + "$ref": "#/components/schemas/ExampleSchema" + }, + "title": "Input 1 Title" + } + }, + "jobControlOptions": [ + "sync-execute", + "async-execute" + ], + "keywords": [ + "example", + "process", + "OGC" + ], + "links": [ + { + "href": "http://example.com/process", + "hreflang": "en", + "rel": "self", + "title": "Process Description", + "type": "application/json" + } + ], + "metadata": [ + { + "href": "http://example.com/metadata", + "hreflang": "en", + "rel": "related", + "title": "Example Metadata", + "type": "application/json" + } + ], + "outputs": { + "output1": { + "description": "Description of Output 1", + "schema": { + "deprecated": false, + "exclusiveMaximum": false, + "exclusiveMinimum": false, + "minItems": 0, + "minLength": 0, + "minProperties": 0, + "nullable": false, + "readOnly": false, + "uniqueItems": false, + "writeOnly": false + }, + "title": "Output 1 Title" + } + }, + "title": "Example Process Title", + "version": "1.0.0" + } +} diff --git a/app-cp/tests/test_api_api.py b/app/tests/test_api_api.py similarity index 99% rename from app-cp/tests/test_api_api.py rename to app/tests/test_api_api.py index 8e29116..52a504f 100644 --- a/app-cp/tests/test_api_api.py +++ b/app/tests/test_api_api.py @@ -1,7 +1,6 @@ # coding: utf-8 from fastapi.testclient import TestClient - from unity_sps_ogc_processes_api.models.enumeration import Enumeration # noqa: F401 from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 diff --git a/app-cp/tests/test_conformance_api.py b/app/tests/test_conformance_api.py similarity index 99% rename from app-cp/tests/test_conformance_api.py rename to app/tests/test_conformance_api.py index 99d2372..9e18c2d 100644 --- a/app-cp/tests/test_conformance_api.py +++ b/app/tests/test_conformance_api.py @@ -1,7 +1,6 @@ # coding: utf-8 from fastapi.testclient import TestClient - from unity_sps_ogc_processes_api.models.conf_classes import ConfClasses # noqa: F401 from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 diff --git a/app-cp/tests/test_dru_api.py b/app/tests/test_dru_api.py similarity index 96% rename from app-cp/tests/test_dru_api.py rename to app/tests/test_dru_api.py index a05433e..ffd5ea8 100644 --- a/app-cp/tests/test_dru_api.py +++ b/app/tests/test_dru_api.py @@ -5,7 +5,6 @@ import pytest from fastapi.testclient import TestClient - from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg @@ -46,7 +45,7 @@ def test_replace(client: TestClient, sample_ogcapppkg): def test_undeploy(client: TestClient): """Test case for undeploy""" process_id = "EchoProcess" - response = client.delete(f"/processes/{process_id}") + response = client.delete(f"/processes/{process_id}", params={"force": True}) assert response.status_code == 204 diff --git a/app-cp/tests/test_jobs_api.py b/app/tests/test_jobs_api.py similarity index 97% rename from app-cp/tests/test_jobs_api.py rename to app/tests/test_jobs_api.py index 522bedf..0fa6d22 100644 --- a/app-cp/tests/test_jobs_api.py +++ b/app/tests/test_jobs_api.py @@ -1,11 +1,8 @@ # coding: utf-8 from fastapi.testclient import TestClient - from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 -from unity_sps_ogc_processes_api.models.inline_or_ref_data import ( # noqa: F401 - InlineOrRefData, -) +from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData # noqa: F401 from unity_sps_ogc_processes_api.models.job_list import JobList # noqa: F401 from unity_sps_ogc_processes_api.models.status_info import StatusInfo # noqa: F401 diff --git a/app-cp/tests/test_landing_page_api.py b/app/tests/test_landing_page_api.py similarity index 99% rename from app-cp/tests/test_landing_page_api.py rename to app/tests/test_landing_page_api.py index d092aee..cb2e455 100644 --- a/app-cp/tests/test_landing_page_api.py +++ b/app/tests/test_landing_page_api.py @@ -1,7 +1,6 @@ # coding: utf-8 from fastapi.testclient import TestClient - from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 from unity_sps_ogc_processes_api.models.landing_page import LandingPage # noqa: F401 diff --git a/app-cp/tests/test_processes_api.py b/app/tests/test_processes_api.py similarity index 89% rename from app-cp/tests/test_processes_api.py rename to app/tests/test_processes_api.py index 914bdd0..faac309 100644 --- a/app-cp/tests/test_processes_api.py +++ b/app/tests/test_processes_api.py @@ -1,22 +1,13 @@ # coding: utf-8 from fastapi.testclient import TestClient - from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 -from unity_sps_ogc_processes_api.models.execute200_response import ( # noqa: F401 - Execute200Response, -) -from unity_sps_ogc_processes_api.models.execute200_response1 import ( # noqa: F401 - Execute200Response1, -) -from unity_sps_ogc_processes_api.models.execute_workflows import ( # noqa: F401 - ExecuteWorkflows, -) +from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response # noqa: F401 +from unity_sps_ogc_processes_api.models.execute200_response1 import Execute200Response1 # noqa: F401 +from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows # noqa: F401 from unity_sps_ogc_processes_api.models.process import Process # noqa: F401 from unity_sps_ogc_processes_api.models.process_list import ProcessList # noqa: F401 -from unity_sps_ogc_processes_api.models.processes_list import ( # noqa: F401 - ProcessesList, -) +from unity_sps_ogc_processes_api.models.processes_list import ProcessesList # noqa: F401 from unity_sps_ogc_processes_api.models.status_info import StatusInfo # noqa: F401 From 52369410f2279ec726ae2321069ff3b9b9e95c79 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Thu, 5 Sep 2024 18:37:52 -0400 Subject: [PATCH 51/60] feat: Initial autogenerated FastAPI implementation --- app/src/unity_sps_ogc_processes_api/models/health_check.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/unity_sps_ogc_processes_api/models/health_check.py b/app/src/unity_sps_ogc_processes_api/models/health_check.py index 7d00fff..cd967f4 100644 --- a/app/src/unity_sps_ogc_processes_api/models/health_check.py +++ b/app/src/unity_sps_ogc_processes_api/models/health_check.py @@ -1,5 +1,7 @@ +from typing import Literal + from pydantic import BaseModel class HealthCheck(BaseModel): - status: str + status: Literal["OK"] From e4987dcca1d18308b45ff205a55fcde9acdefd47 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Mon, 9 Sep 2024 12:47:06 -0700 Subject: [PATCH 52/60] feat: Initial autogenerated FastAPI implementation --- app/src/openapi_server/database/models.py | 5 +- app/src/openapi_server/impl/dru_api.py | 76 ++++++++++++++--------- 2 files changed, 51 insertions(+), 30 deletions(-) diff --git a/app/src/openapi_server/database/models.py b/app/src/openapi_server/database/models.py index eca7fba..c24ee22 100644 --- a/app/src/openapi_server/database/models.py +++ b/app/src/openapi_server/database/models.py @@ -17,7 +17,6 @@ class Process(Base): links = Column(JSON) inputs = Column(JSON) outputs = Column(JSON) - deployment_status = Column(String, default="pending") jobs = relationship( "Job", back_populates="process", @@ -51,7 +50,9 @@ class ExecutionUnit(Base): class Ogcapppkg(Base): __tablename__ = "ogcapppkgs" _id = Column(Integer, primary_key=True) - process_id = Column(String, ForeignKey("processes.id", ondelete="CASCADE"), nullable=False) + process_id = Column( + String, ForeignKey("processes.id", ondelete="CASCADE"), nullable=False + ) process = relationship("Process", back_populates="ogcapppkg", passive_deletes=True) execution_unit = relationship( "ExecutionUnit", diff --git a/app/src/openapi_server/impl/dru_api.py b/app/src/openapi_server/impl/dru_api.py index 0e84744..1a2a034 100644 --- a/app/src/openapi_server/impl/dru_api.py +++ b/app/src/openapi_server/impl/dru_api.py @@ -4,13 +4,14 @@ import requests from fastapi import HTTPException, Response, status -from openapi_server.config.config import Settings -from openapi_server.database import crud -from openapi_server.utils.redis import RedisLock from redis.exceptions import LockError from requests.auth import HTTPBasicAuth from sqlalchemy.orm import Session from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound + +from openapi_server.config.config import Settings +from openapi_server.database import crud +from openapi_server.utils.redis import RedisLock from unity_sps_ogc_processes_api.apis.dru_api_base import BaseDRUApi from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg @@ -41,7 +42,9 @@ def check_process_integrity(db: Session, process_id: str, new_process: bool): class DRUApiImpl(BaseDRUApi): - def __init__(self, settings: Settings, redis_locking_client: RedisLock, db: Session): + def __init__( + self, settings: Settings, redis_locking_client: RedisLock, db: Session + ): self.settings = settings self.redis_locking_client = redis_locking_client self.db = db @@ -50,12 +53,14 @@ def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: lock_key = f"process:{ogcapppkg.process_description.id}" try: with self.redis_locking_client.lock(lock_key, lock_timeout=60): - check_process_integrity(self.db, ogcapppkg.process_description.id, new_process=True) - # ogcapppkg.process_description.deployment_status = "deploying" - crud.create_process(self.db, ogcapppkg) + check_process_integrity( + self.db, ogcapppkg.process_description.id, new_process=True + ) dag_filename = ogcapppkg.process_description.id + ".py" - dag_catalog_filepath = os.path.join(self.settings.DAG_CATALOG_DIRECTORY, dag_filename) + dag_catalog_filepath = os.path.join( + self.settings.DAG_CATALOG_DIRECTORY, dag_filename + ) if not os.path.isfile(dag_catalog_filepath): existing_files = os.listdir(self.settings.DAG_CATALOG_DIRECTORY) existing_files_str = "\n".join(existing_files) @@ -64,7 +69,9 @@ def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: detail=f"The process ID '{ogcapppkg.process_description.id}' does not have a matching DAG file named '{dag_filename}' in the DAG catalog.\nThe DAG catalog includes the following files:\n{existing_files_str}", ) - if os.path.isfile(os.path.join(self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename)): + if os.path.isfile( + os.path.join(self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename) + ): # TODO Log warning that file already exists in the deployed dags directory pass @@ -73,7 +80,9 @@ def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: self.settings.DEPLOYED_DAGS_DIRECTORY, ) - if not os.path.isfile(os.path.join(self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename)): + if not os.path.isfile( + os.path.join(self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename) + ): raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Failed to copy DAG file to deployed directory", @@ -108,12 +117,7 @@ def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: status_code=status.HTTP_504_GATEWAY_TIMEOUT, detail=f"Timeout waiting for DAG '{ogcapppkg.process_description.id}' to be available in Airflow.", ) - - crud.update_process( - self.db, - ogcapppkg.process_description.id, - {"deployment_status": "deployed"}, - ) + crud.create_process(self.db, ogcapppkg) return Response( status_code=status.HTTP_201_CREATED, @@ -128,7 +132,9 @@ def deploy(self, ogcapppkg: Ogcapppkg, w: str) -> Response: # Re-raise HTTPExceptions without wrapping them raise except Exception as e: - raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: lock_key = f"process:{processId}" @@ -143,12 +149,18 @@ def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: ) # Update the existing process with new data - crud.update_process(self.db, processId, ogcapppkg.process_description.model_dump()) + crud.update_process( + self.db, processId, ogcapppkg.process_description.model_dump() + ) # Update the DAG file dag_filename = f"{processId}.py" - dag_catalog_filepath = os.path.join(self.settings.DAG_CATALOG_DIRECTORY, dag_filename) - deployed_dag_path = os.path.join(self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename) + dag_catalog_filepath = os.path.join( + self.settings.DAG_CATALOG_DIRECTORY, dag_filename + ) + deployed_dag_path = os.path.join( + self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename + ) if os.path.exists(dag_catalog_filepath): shutil.copy2(dag_catalog_filepath, deployed_dag_path) @@ -183,7 +195,9 @@ def replace(self, processId: str, ogcapppkg: Ogcapppkg) -> None: # Re-raise HTTPExceptions without wrapping them raise except Exception as e: - raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) def undeploy(self, processId: str, force: bool = False) -> None: lock_key = f"process:{processId}" @@ -207,7 +221,9 @@ def undeploy(self, processId: str, force: bool = False) -> None: ) # Pause the DAG - self.pause_dag(self.settings.EMS_API_URL, processId, ems_api_auth, pause=True) + self.pause_dag( + self.settings.EMS_API_URL, processId, ems_api_auth, pause=True + ) # Get active DAG runs again after the DAG is paused active_dag_runs = self.list_active_dag_runs( @@ -230,7 +246,9 @@ def undeploy(self, processId: str, force: bool = False) -> None: # Remove the DAG file from the deployed directory dag_filename = f"{processId}.py" - deployed_dag_path = os.path.join(self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename) + deployed_dag_path = os.path.join( + self.settings.DEPLOYED_DAGS_DIRECTORY, dag_filename + ) if os.path.isfile(deployed_dag_path): try: os.remove(deployed_dag_path) @@ -277,7 +295,9 @@ def undeploy(self, processId: str, force: bool = False) -> None: raise except Exception as e: # For any other exception, wrap it in a generic HTTPException - raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) def pause_dag(self, airflow_url, dag_id, auth, pause=True): endpoint = f"{airflow_url}/dags/{dag_id}" @@ -304,9 +324,9 @@ def stop_task_instances(self, airflow_url, dag_id, dag_run_id, auth): tasks.raise_for_status() for task in tasks.json()["task_instances"]: - task_instance_endpoint = ( - f"{airflow_url}/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task['task_id']}" - ) + task_instance_endpoint = f"{airflow_url}/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task['task_id']}" update_data = {"dry_run": False, "new_state": "failed"} - update_response = requests.patch(task_instance_endpoint, auth=auth, json=update_data) + update_response = requests.patch( + task_instance_endpoint, auth=auth, json=update_data + ) update_response.raise_for_status() From 4f7d5506698b9ad252f0adc1ca6beec75992a9cb Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 10 Sep 2024 08:41:00 -0700 Subject: [PATCH 53/60] feat: Initial autogenerated FastAPI implementation --- app/src/openapi_server/impl/processes_api.py | 51 +++++++++++++++----- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/app/src/openapi_server/impl/processes_api.py b/app/src/openapi_server/impl/processes_api.py index 6ceea09..aec7d6a 100644 --- a/app/src/openapi_server/impl/processes_api.py +++ b/app/src/openapi_server/impl/processes_api.py @@ -3,13 +3,14 @@ import requests from fastapi import HTTPException, status +from redis.exceptions import LockError +from requests.auth import HTTPBasicAuth +from sqlalchemy.orm import Session + from openapi_server.config.config import Settings from openapi_server.database import crud from openapi_server.impl.dru_api import check_process_integrity from openapi_server.utils.redis import RedisLock -from redis.exceptions import LockError -from requests.auth import HTTPBasicAuth -from sqlalchemy.orm import Session from unity_sps_ogc_processes_api.apis.processes_api_base import BaseProcessesApi from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows @@ -25,7 +26,9 @@ class ProcessesApiImpl(BaseProcessesApi): - def __init__(self, settings: Settings, redis_locking_client: RedisLock, db: Session): + def __init__( + self, settings: Settings, redis_locking_client: RedisLock, db: Session + ): self.settings = settings self.redis_locking_client = redis_locking_client self.db = db @@ -42,16 +45,28 @@ def get_process_description(self, processId: str) -> Process: # Convert metadata, links, inputs, and outputs if they exist metadata = ( - [Metadata.model_validate(m) for m in process.metadata] if process.metadata else None + [Metadata.model_validate(m) for m in process.metadata] + if process.metadata + else None + ) + links = ( + [Link.model_validate(link) for link in process.links] + if process.links + else None ) - links = [Link.model_validate(link) for link in process.links] if process.links else None inputs = ( - {k: InputDescription.model_validate(v) for k, v in process.inputs.items()} + { + k: InputDescription.model_validate(v) + for k, v in process.inputs.items() + } if process.inputs else None ) outputs = ( - {k: OutputDescription.model_validate(v) for k, v in process.outputs.items()} + { + k: OutputDescription.model_validate(v) + for k, v in process.outputs.items() + } if process.outputs else None ) @@ -74,7 +89,9 @@ def get_process_description(self, processId: str) -> Process: detail="Unable to acquire lock. Please try again later.", ) except Exception as e: - raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) def get_processes(self) -> ProcessList: processes = crud.get_processes(self.db) @@ -84,7 +101,11 @@ def get_processes(self) -> ProcessList: id=process.id, title=process.title, description=process.description, + keywords=process.keywords, + metadata=process.metadata, version=process.version, + job_control_options=process.job_control_options, + links=process.links, ) for process in processes ], @@ -171,7 +192,9 @@ def execute( # Note: In a real-world scenario, you'd wait for the job to complete # and return the actual results. This is a simplified version. # TODO get result from job using the result endpoint and return it here. - return Execute200Response({"result": "Sample output for synchronous execution"}) + return Execute200Response( + {"result": "Sample output for synchronous execution"} + ) except LockError: raise HTTPException( @@ -180,11 +203,15 @@ def execute( ) except requests.exceptions.RequestException as e: status_code_to_raise = status.HTTP_500_INTERNAL_SERVER_ERROR - detail_message = f"Failed to start DAG run {job_id} with DAG {processId}: {str(e)}" + detail_message = ( + f"Failed to start DAG run {job_id} with DAG {processId}: {str(e)}" + ) if hasattr(e, "response"): detail_message = f"Failed to start DAG run {job_id} with DAG {processId}: {e.response.status_code} {e.response.reason}" raise HTTPException(status_code=status_code_to_raise, detail=detail_message) except Exception as e: - raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) From a34818d052c19a1e2613a37a8805867d887a46ad Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 17 Sep 2024 15:54:55 -0700 Subject: [PATCH 54/60] Updated ogcapi-processes submodule to point to new repository --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 03e8bb3..63ebee2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "ogcapi-processes"] path = ogcapi-processes - url = https://github.com/opengeospatial/ogcapi-processes + url = https://github.com/drewm-jpl/ogcapi-processes From 72869f8c2e0f889eaf3eca4f67e03c5d94b1c8c2 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 17 Sep 2024 15:55:45 -0700 Subject: [PATCH 55/60] Updated ogcapi-processes submodule to point to new repository --- ogcapi-processes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ogcapi-processes b/ogcapi-processes index d011fe9..b37c67f 160000 --- a/ogcapi-processes +++ b/ogcapi-processes @@ -1 +1 @@ -Subproject commit d011fe92750a0843106fcb87b6292db6e2b0fa51 +Subproject commit b37c67f1aecc6c16e5b47702cd9db4e9611fe0b8 From e40fa8d3562954ca9e2792bdccc01b4c69a39e65 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 17 Sep 2024 15:59:56 -0700 Subject: [PATCH 56/60] chore: Fix static analysis issues --- app-cp/__init__.py | 0 app-cp/config.py | 13 - app-cp/database/__init__.py | 13 - app-cp/database/crud.py | 61 -- app-cp/database/models.py | 58 -- app-cp/main.py | 629 ------------------ app-cp/redis.py | 25 - app-cp/schemas/__init__.py | 0 app-cp/schemas/ogc_processes.py | 608 ----------------- app-cp/schemas/unity_sps.py | 7 - app/src/openapi_server/database/crud.py | 1 + app/src/openapi_server/impl/api_api.py | 1 + app/src/openapi_server/impl/jobs_api.py | 7 +- .../apis/api_api.py | 3 +- .../apis/conformance_api.py | 3 +- .../apis/dru_api.py | 11 +- .../apis/health_api.py | 3 +- .../apis/jobs_api.py | 11 +- .../apis/landing_page_api.py | 3 +- .../apis/processes_api.py | 11 +- app/src/unity_sps_ogc_processes_api/main.py | 15 +- .../models/bbox.py | 1 + .../models/bbox1.py | 1 + .../models/bbox_processes.py | 1 + .../models/collection_info.py | 5 +- .../models/collections.py | 1 + .../unity_sps_ogc_processes_api/models/crs.py | 1 + .../models/crs_one_of.py | 1 + .../models/description_type.py | 1 + .../models/execute.py | 1 + .../models/execute200_response.py | 1 + .../models/execute200_response1.py | 10 +- .../models/execute_workflows.py | 5 +- .../models/execute_workflows1.py | 5 +- .../models/execution_unit.py | 1 + .../models/extent.py | 1 + .../models/extent_spatial.py | 14 +- .../models/extent_spatial_grid_inner.py | 1 + ...nt_spatial_grid_inner_coordinates_inner.py | 9 +- .../extent_spatial_grid_inner_resolution.py | 9 +- .../models/extent_temporal.py | 1 + .../models/extent_temporal_grid.py | 5 +- .../models/extent_temporal_grid_resolution.py | 9 +- .../models/extent_uad.py | 1 + .../models/feature_collection.py | 10 +- .../models/fields_modifiers.py | 5 +- .../models/format.py | 1 + .../models/geo_json_feature.py | 14 +- .../models/geo_json_feature_geometry.py | 9 +- .../models/geo_json_feature_id.py | 9 +- .../models/geo_json_line_string.py | 9 +- .../models/geo_json_multi_line_string.py | 9 +- .../models/geo_json_multi_point.py | 9 +- .../models/geo_json_multi_polygon.py | 9 +- .../models/geo_json_point.py | 9 +- .../models/geo_json_polygon.py | 9 +- .../models/inline_or_ref_data.py | 1 + .../models/inline_or_ref_data1.py | 9 +- .../models/inline_or_ref_data_workflows.py | 9 +- .../models/input.py | 5 +- .../models/input_collection.py | 5 +- .../models/input_description.py | 1 + .../models/input_parameterized.py | 5 +- .../models/input_process.py | 5 +- .../models/input_value.py | 1 + .../models/input_value1.py | 5 +- .../models/input_value_no_object.py | 1 + .../models/input_value_no_object1.py | 1 + .../models/input_value_no_object_workflows.py | 1 + .../models/input_value_workflows.py | 4 +- .../models/input_workflows.py | 5 +- .../models/input_workflows1.py | 4 +- .../models/job_list.py | 1 + .../models/landing_page.py | 1 + .../models/metadata.py | 1 + .../models/metadata_one_of1.py | 5 +- .../models/model_schema.py | 1 + .../models/ogcapppkg.py | 5 +- .../models/ogcapppkg_array_inner.py | 1 + .../models/ogcapppkg_execution_unit.py | 1 + .../models/output.py | 1 + .../models/output_description.py | 1 + .../models/output_workflows.py | 1 + .../models/output_workflows1.py | 1 + .../models/process.py | 1 + .../models/process_list.py | 1 + .../models/process_summary.py | 1 + .../models/qualified_input_value.py | 1 + .../models/qualified_input_value1.py | 1 + .../models/qualified_input_value_workflows.py | 5 +- .../models/schema1.py | 1 + .../models/schema_one_of.py | 10 +- .../models/static_indicator.py | 1 + .../models/status_info.py | 1 + .../security_api.py | 6 +- app/tests/conftest.py | 13 +- app/tests/test_api_api.py | 1 + app/tests/test_conformance_api.py | 1 + app/tests/test_dru_api.py | 1 + app/tests/test_jobs_api.py | 5 +- app/tests/test_landing_page_api.py | 1 + app/tests/test_processes_api.py | 17 +- unity-test/conftest.py | 9 +- unity-test/test_main.py | 5 +- 104 files changed, 346 insertions(+), 1488 deletions(-) delete mode 100644 app-cp/__init__.py delete mode 100644 app-cp/config.py delete mode 100644 app-cp/database/__init__.py delete mode 100644 app-cp/database/crud.py delete mode 100644 app-cp/database/models.py delete mode 100644 app-cp/main.py delete mode 100644 app-cp/redis.py delete mode 100644 app-cp/schemas/__init__.py delete mode 100644 app-cp/schemas/ogc_processes.py delete mode 100644 app-cp/schemas/unity_sps.py diff --git a/app-cp/__init__.py b/app-cp/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app-cp/config.py b/app-cp/config.py deleted file mode 100644 index 3d42716..0000000 --- a/app-cp/config.py +++ /dev/null @@ -1,13 +0,0 @@ -from pydantic import HttpUrl, SecretStr -from pydantic_settings import BaseSettings - - -class Settings(BaseSettings): - DB_URL: str = "sqlite:///:memory:" - REDIS_HOST: str = "http://localhost" - REDIS_PORT: int = 6379 - EMS_API_URL: HttpUrl = "http://localhost:8080/api/v1" - EMS_API_AUTH_USERNAME: str = "username" - EMS_API_AUTH_PASSWORD: SecretStr = "password" - DAG_CATALOG_DIRECTORY: str = "/dag-catalog" - DEPLOYED_DAGS_DIRECTORY: str = "/deployed-dags" diff --git a/app-cp/database/__init__.py b/app-cp/database/__init__.py deleted file mode 100644 index 616c5c3..0000000 --- a/app-cp/database/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from sqlalchemy import create_engine -from sqlalchemy.orm import declarative_base, sessionmaker - -from .. import config - -settings = config.Settings() - -SQLALCHEMY_DATABASE_URL = settings.DB_URL - -engine = create_engine(SQLALCHEMY_DATABASE_URL) -SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) - -Base = declarative_base() diff --git a/app-cp/database/crud.py b/app-cp/database/crud.py deleted file mode 100644 index 210a78a..0000000 --- a/app-cp/database/crud.py +++ /dev/null @@ -1,61 +0,0 @@ -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import Session - -from ..schemas import ogc_processes -from . import models - - -def create_process(db: Session, process: ogc_processes.Process): - db_process = models.Process(**process.model_dump()) - db.add(db_process) - db.commit() - db.refresh(db_process) - return db_process - - -def get_processes(db: Session, skip: int = 0, limit: int = 100): - return db.query(models.Process).offset(skip).limit(limit).all() - - -def get_process(db: Session, process_id: str): - return db.query(models.Process).filter(models.Process.id == process_id).one() - - -def delete_process(db: Session, process: models.Process): - db.delete(process) - db.commit() - - -def create_job(db: Session, execute: ogc_processes.Execute, job: ogc_processes.StatusInfo): - db_job = models.Job(**execute.model_dump(mode="json"), **job.model_dump()) - db.add(db_job) - db.commit() - db.refresh(db_job) - return db_job - - -def update_job(db: Session, job: ogc_processes.StatusInfo): - db_job = db.query(models.Job).filter(models.Job.jobID == job.jobID).one() - for key, value in job.model_dump().items(): - if hasattr(db_job, key): - setattr(db_job, key, value) - db.commit() - db.refresh(db_job) - return db_job - - -def get_jobs(db: Session, skip: int = 0, limit: int = 100): - return db.query(models.Job).offset(skip).limit(limit).all() - - -def get_job(db: Session, job_id: UUID): - return db.query(models.Job).filter(models.Job.jobID == job_id).one() - - -def get_results(db: Session, job_id: UUID): - return db.query(models.Result).filter(models.Result.jobID == job_id).all() - - -def delete_job(db: Session, job: models.Job): - db.delete(job) - db.commit() diff --git a/app-cp/database/models.py b/app-cp/database/models.py deleted file mode 100644 index a13b29d..0000000 --- a/app-cp/database/models.py +++ /dev/null @@ -1,58 +0,0 @@ -from sqlalchemy import JSON, Column, DateTime, ForeignKey, Integer, String -from sqlalchemy.orm import relationship -from sqlalchemy.sql import func - -from . import Base - - -class Process(Base): - __tablename__ = "processes" - _id = Column(Integer, primary_key=True) - jobs = relationship("Job", back_populates="process") - - id = Column(String, index=True, unique=True, nullable=False) - title = Column(String) - description = Column(String) - keywords = Column(JSON) - version = Column(String) - jobControlOptions = Column(JSON) - links = Column(JSON) - inputs = Column(JSON) - outputs = Column(JSON) - - -# https://docs.sqlalchemy.org/en/20/orm/declarative_tables.html#appending-additional-columns-to-an-existing-declarative-mapped-class -Process.metadata = Column("metadata", JSON) - - -class Job(Base): - __tablename__ = "jobs" - _id = Column(Integer, primary_key=True) - jobID = Column(String, index=True, unique=True, nullable=False) - processID = Column(String, ForeignKey("processes.id")) - process = relationship("Process", back_populates="jobs") - results = relationship("Result", back_populates="job") - - type = Column(String) - status = Column(String) - message = Column(String, nullable=True) - exception = Column(JSON, nullable=True) - created = Column(DateTime(timezone=True), default=func.now()) - started = Column(DateTime(timezone=True), nullable=True) - finished = Column(DateTime(timezone=True), nullable=True) - updated = Column(DateTime(timezone=True), nullable=True) - progress = Column(Integer) - links = Column(JSON, nullable=True) - - inputs = Column(JSON) - outputs = Column(JSON) - subscriber = Column(JSON) - - -class Result(Base): - __tablename__ = "results" - _id = Column(Integer, primary_key=True) - jobID = Column(String, ForeignKey("jobs.jobID")) - job = relationship("Job", back_populates="results") - - root = Column(JSON) diff --git a/app-cp/main.py b/app-cp/main.py deleted file mode 100644 index 7005533..0000000 --- a/app-cp/main.py +++ /dev/null @@ -1,629 +0,0 @@ -# generated by fastapi-codegen: -# filename: ogcapi-processes.yaml -# timestamp: 2024-02-16T19:06:21+00:00 - -from __future__ import annotations - -import os -import shutil -import time -import uuid -from datetime import datetime -from functools import lru_cache - -import requests -from fastapi import BackgroundTasks, Body, Depends, FastAPI, HTTPException -from fastapi import status as fastapi_status -from requests.auth import HTTPBasicAuth -from sqlalchemy.orm import Session -from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound -from typing_extensions import Annotated - -from . import config -from .database import SessionLocal, crud, engine, models -from .redis import RedisLock -from .schemas.ogc_processes import ( - ConfClasses, - Execute, - JobList, - LandingPage, - Link, - Process, - ProcessList, - ProcessSummary, - Results, - StatusCode, - StatusInfo, - Type2, -) -from .schemas.unity_sps import HealthCheck - -models.Base.metadata.create_all(bind=engine) # Create database tables - - -app = FastAPI( - version="1.0.0", - title="Unity Processing API conforming to the OGC API - Processes - Part 1 standard", - description="This document is an API definition document provided alongside the OGC API - Processes standard. The OGC API - Processes Standard specifies a processing interface to communicate over a RESTful protocol using JavaScript Object Notation (JSON) encodings. The specification allows for the wrapping of computational tasks into executable processes that can be offered by a server and be invoked by a client application.", - # contact={"name": "Placeholder", "email": "Placeholder"}, - license={"name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html"}, - servers=[], -) - - -@lru_cache -def get_settings(): - return config.Settings() - - -@lru_cache() -def get_redis_locking_client(): - settings = get_settings() - return RedisLock(host=settings.REDIS_HOST, port=settings.REDIS_PORT) - - -# @lru_cache() -# def get_ems_client(): -# settings = get_settings() -# configuration = Configuration( -# host=settings.EMS_API_URL, -# username=settings.EMS_API_AUTH_USERNAME, -# password=settings.EMS_API_AUTH_PASSWORD.get_secret_value(), -# ) -# return ApiClient(configuration) - - -def get_db(): - db = SessionLocal() - try: - yield db - finally: - db.close() - - -def check_process_integrity(db: Session, process_id: str, new_process: bool): - process = None - try: - process = crud.get_process(db, process_id) - # TODO If not a new process, check if deployment_status is complete - # If not, raise an exception that it it's deployment status is not complete - if new_process and process is not None: - raise ValueError - except NoResultFound: - if not new_process: - raise HTTPException( - status_code=fastapi_status.HTTP_404_NOT_FOUND, - detail=f"Process with ID '{process_id}' not found", - ) - except MultipleResultsFound: - raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Multiple processes found with same ID '{process_id}', data integrity error", - ) - except ValueError: - raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Existing process with ID '{process_id}' already exists", - ) - return process - - -def check_job_integrity(db: Session, job_id: str, new_job: bool): - job = None - try: - job = crud.get_job(db, job_id) - if new_job and job is not None: - raise ValueError - except NoResultFound: - if not new_job: - raise HTTPException( - status_code=fastapi_status.HTTP_404_NOT_FOUND, detail=f"Job with ID '{job_id}' not found" - ) - except MultipleResultsFound: - raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Multiple jobs found with same ID '{job_id}', data integrity error", - ) - except ValueError: - raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Existing job with ID '{job_id}' already exists", - ) - return job - - -@app.get("/", response_model=LandingPage, summary="Landing page of this API") -async def landing_page(): - """ - The landing page provides links to the: - - API Definition (no fixed path), - - Conformance Statements (`/conformance`), - - Processes Metadata (`/processes`), - - Endpoint for Job Monitoring (`/jobs`). - - For more information, see [Section 7.2](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_landing_page). - """ - return LandingPage( - title="Unity SPS Processing Server", - description="Server implementing the OGC API - Processes 1.0 Standard", - links=[Link(href="/conformance"), Link(href="/processes"), Link(href="/jobs")], - ) - - -@app.get( - "/health", - summary="Perform a Health Check", - response_description="Return HTTP Status Code 200 (OK)", - status_code=fastapi_status.HTTP_200_OK, - response_model=HealthCheck, -) -def get_health() -> HealthCheck: - """ - Endpoint to perform a healthcheck on. This endpoint can primarily be used Docker - to ensure a robust container orchestration and management is in place. Other - services which rely on proper functioning of the API service will not deploy if this - endpoint returns any other HTTP status code except 200 (OK). - Returns: - HealthCheck: Returns a JSON response with the health status - """ - return HealthCheck(status="OK") - - -@app.get( - "/conformance", - response_model=ConfClasses, - summary="Information about standards that this API conforms to", -) -async def conformance_declaration(): - """ - A list of all conformance classes, specified in a standard, that the server conforms to. - - | Conformance class | URI | - | -------- | ------- | - | Core | http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/core | - | OGC Process Description | http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/ogc-process-description | - | JSON | http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/json | - | HTML | http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/html | - | OpenAPI | Specification 3.0 http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/oas30 | - | Job list | http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/job-list | - | Callback | http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/callback | - | Dismiss | http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/dismiss | - - For more information, see [Section 7.4](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_conformance_classes). - """ - return ConfClasses( - conformsTo=["http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/ogc-process-description"] - ) - - -def pause_dag(airflow_url, dag_id, auth, pause=True): - """Pauses or unpauses a DAG based on the pause parameter.""" - endpoint = f"{airflow_url}/dags/{dag_id}" - data = {"is_paused": pause} - response = requests.patch(endpoint, auth=auth, json=data) - response.raise_for_status() - - -def list_active_dag_runs(airflow_url, dag_id, auth): - """Fetches all active DAG runs for a specific DAG.""" - endpoint = f"{airflow_url}/dags/{dag_id}/dagRuns" - params = {"state": "running"} # Adjust the states as necessary - response = requests.get(endpoint, auth=auth, params=params) - response.raise_for_status() - return response.json()["dag_runs"] - - -def stop_dag_run(airflow_url, dag_id, dag_run_id, auth): - """Stops a specific DAG run by updating its state to 'failed'.""" - endpoint = f"{airflow_url}/dags/{dag_id}/dagRuns/{dag_run_id}" - data = {"state": "failed"} # Use 'failed' or another terminal state - response = requests.patch(endpoint, auth=auth, json=data) - response.raise_for_status() - - -def stop_task_instances(airflow_url, dag_id, dag_run_id, auth): - """Stops all task instances of a specific DAG run.""" - endpoint = f"{airflow_url}/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances" - tasks = requests.get(endpoint, auth=auth) - tasks.raise_for_status() - - for task in tasks.json()["task_instances"]: - task_instance_endpoint = ( - f"{airflow_url}/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task['task_id']}" - ) - update_data = {"dry_run": False, "new_state": "failed"} - update_response = requests.patch(task_instance_endpoint, auth=auth, json=update_data) - update_response.raise_for_status() - - -# def deploy_process_background( -# settings: config.Settings, -# db: Session, -# process: Process, -# ): -# lock_id = f"process_deploy_{process.id}" -# try: -# with redis_lock.lock(lock_id, timeout=20): -# check_process_integrity(db, process.id, new_process=True) -# # Add the actual deployment logic here -# # For example, file copying, Airflow DAG interaction, etc. -# crud.create_process(db, process) -# except LockError as e: -# raise HTTPException(status_code=409, detail=str(e)) - - -# @app.post("/processes", response_model=Process, summary="Deploy a process") -# def deploy_process( -# background_tasks: BackgroundTasks, -# settings: Annotated[config.Settings, Depends(get_settings)], -# db: Session = Depends(get_db), -# process: Process = Body(...), -# ): -# background_tasks.add_task(deploy_process_background, settings, db, process) -# return {"message": "Process deployment initiated."} - - -@app.post("/processes", response_model=Process, summary="Deploy a process") -def deploy_process( - background_tasks: BackgroundTasks, - settings: Annotated[config.Settings, Depends(get_settings)], - redis_locking_client: Annotated[RedisLock, Depends(get_redis_locking_client)], - db: Session = Depends(get_db), - process: Process = Body(...), -): - """ - Deploy a new process. - - **Note:** This is not an officially supported endpoint in the OGC Processes specification. - """ - check_process_integrity(db, process.id, new_process=True) - - with redis_locking_client.lock("deploy_process_" + process.id): # as lock: - pass - - # Acquire lock - # Create process in DB w/ deployment_status field "deploying" - # Check if DAG exists in Airflow - # Check if file exists in DAG folder - # Check if file exists in DAG catalog - # Copy file to DAG folder - # Poll Airflow until DAG appears - # Unpause DAG - # Check if DAG is_active is True - # Update process in DB w/ deployment_status field "deployed" - # Release lock - - # Verify that the process_id corresponds with a DAG ID by filename in the DAG catalog - dag_filename = process.id + ".py" - dag_catalog_filepath = os.path.join(settings.DAG_CATALOG_DIRECTORY, dag_filename) - if not os.path.isfile(dag_catalog_filepath): - # If the file doesn't exist, list other files in the same directory - existing_files = os.listdir(settings.DAG_CATALOG_DIRECTORY) - existing_files_str = "\n".join(existing_files) # Create a string from the list of files - - # Raise an exception with details about what files are actually there - raise HTTPException( - status_code=fastapi_status.HTTP_409_CONFLICT, - detail=f"The process ID '{process.id}' does not have a matching DAG file named '{dag_filename}' in the DAG catalog.\nThe DAG catalog includes the following files:\n{existing_files_str}", - ) - - if os.path.isfile(os.path.join(settings.DEPLOYED_DAGS_DIRECTORY, dag_filename)): - # Log warning that file already exists in the deployed dags directory - pass - - # Copy DAG from the DAG catalog PVC to deployed PVC - shutil.copy2( - dag_catalog_filepath, - settings.DEPLOYED_DAGS_DIRECTORY, - ) - - if not os.path.isfile(os.path.join(settings.DEPLOYED_DAGS_DIRECTORY, dag_filename)): - raise HTTPException( - status_code=fastapi_status.HTTP_409_CONFLICT, - detail="", - ) - - # Poll the EMS API to verify DAG existence - ems_api_auth = HTTPBasicAuth( - settings.EMS_API_AUTH_USERNAME, settings.EMS_API_AUTH_PASSWORD.get_secret_value() - ) - timeout = 20 - start_time = time.time() - while time.time() - start_time < timeout: - response = requests.get(f"{settings.EMS_API_URL}/dags/{process.id}", auth=ems_api_auth) - data = response.json() - if response.status_code == 404: - pass - elif data["is_paused"]: - pause_dag(settings.EMS_API_URL, process.id, ems_api_auth, pause=False) - elif data["is_active"]: - break - time.sleep(0.5) - else: - raise HTTPException( - status_code=fastapi_status.HTTP_504_GATEWAY_TIMEOUT, - detail=f"Timeout waiting for DAG '{process.id}' to be available in Airflow.", - ) - - return crud.create_process(db, process) - - -@app.delete( - "/processes/{process_id}", status_code=fastapi_status.HTTP_204_NO_CONTENT, summary="Undeploy a process" -) -def undeploy_process( - background_tasks: BackgroundTasks, - settings: Annotated[config.Settings, Depends(get_settings)], - redis_locking_client: Annotated[RedisLock, Depends(get_redis_locking_client)], - process_id: str, - db: Session = Depends(get_db), - force: bool = False, -): - """ - Undeploy an existing process. - - **Note:** This is not an officially supported endpoint in the OGC Processes specification. - """ - process = check_process_integrity(db, process_id, new_process=False) - - with redis_locking_client.lock("deploy_process_" + process.id): # as lock: - pass - - # Acquire lock - # Update process in DB w/ deployment_status field "undeploying" - # Check if DAG exists in Airflow - # Pause the DAG - # Stop all DAG runs and tasks - # Remove file from dag folder - # Ensure DAG field is_active turns to False - # Delete process from DB - # Release lock - - ems_api_auth = HTTPBasicAuth( - settings.EMS_API_AUTH_USERNAME, settings.EMS_API_AUTH_PASSWORD.get_secret_value() - ) - # response = requests.get(f"{settings.EMS_API_URL}/dags/{process_id}", auth=ems_api_auth) - # if response.status_code == 200: - # return True # DAG exists - # elif response.status_code == 404: - # return False # DAG does not exist - # else: - # response.raise_for_status() # Raise an exception for other HTTP errors - - active_dag_runs = list_active_dag_runs(settings.EMS_API_URL, process_id, ems_api_auth) - if len(active_dag_runs) and not force: - raise HTTPException( - status_code=fastapi_status.HTTP_409_CONFLICT, - detail="Process has active DAG runs. Set 'force' to true to override and stop all active DAG runs and tasks.", - ) - - # Pause the DAG first - pause_dag(settings.EMS_API_URL, process_id, ems_api_auth, pause=True) - - for dag_run in active_dag_runs: - stop_dag_run(settings.EMS_API_URL, process_id, dag_run["dag_run_id"], ems_api_auth) - stop_task_instances(settings.EMS_API_URL, process_id, dag_run["dag_run_id"], ems_api_auth) - - dag_filename = process_id + ".py" - deployed_dag_filepath = os.path.join(settings.DEPLOYED_DAGS_DIRECTORY, dag_filename) - if os.path.isfile(deployed_dag_filepath): - try: - os.remove(deployed_dag_filepath) - except OSError as e: - raise HTTPException( - status_code=fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to remove DAG file from deployed DAGs directory: {e.strerror}", - ) - - # Poll for the removal of the DAG from the Airflow API - timeout = 20 - start_time = time.time() - while time.time() - start_time < timeout: - response = requests.get(f"{settings.EMS_API_URL}/dags/{process_id}", auth=ems_api_auth) - data = response.json() - if response.status_code == 404: - break - elif not data["is_active"]: - break - time.sleep(0.5) - else: - raise HTTPException( - status_code=fastapi_status.HTTP_504_GATEWAY_TIMEOUT, - detail="Timeout waiting for DAG to be fully removed from Airflow.", - ) - - crud.delete_process(db, process) - - -@app.get("/processes", response_model=ProcessList, summary="Retrieve the list of available processes") -def process_list(db: Session = Depends(get_db)): - """ - The list of processes contains a summary of each process the OGC API - Processes offers, including the link to a more detailed description of the process. - - For more information, see [Section 7.9](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_process_list). - """ - processes = crud.get_processes(db) - process_summaries = [] - for p in processes: - process_summaries.append(ProcessSummary(**Process.model_validate(p).model_dump())) - links = [ - Link(href="/processes", rel="self", type="application/json", hreflang=None, title="List of processes") - ] - return ProcessList(processes=process_summaries, links=links) - - -@app.get("/processes/{process_id}", response_model=Process, summary="Retrieve a process description") -def process_description(process_id: str, db: Session = Depends(get_db)): - """ - The process description contains information about inputs and outputs and a link to the execution-endpoint for the process. The Core does not mandate the use of a specific process description to specify the interface of a process. That said, the Core requirements class makes the following recommendation: - - Implementations SHOULD consider supporting the OGC process description. - - For more information, see [Section 7.10](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_process_description). - """ - return check_process_integrity(db, process_id, new_process=False) - - -@app.get("/jobs", response_model=JobList, summary="Retrieve the list of jobs") -def job_list(db: Session = Depends(get_db)): - """ - Lists available jobs. - - For more information, see [Section 11](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_job_list). - """ - jobs = crud.get_jobs(db) - links = [Link(href="/jobs", rel="self", type="application/json", hreflang=None, title="List of jobs")] - return JobList(jobs=jobs, links=links) - - -@app.post("/processes/{process_id}/execution", response_model=StatusInfo, summary="Execute a process") -def execute( - settings: Annotated[config.Settings, Depends(get_settings)], - process_id: str, - execute: Execute = Body(...), - db: Session = Depends(get_db), -): - """ - Create a new job. - - For more information, see [Section 7.11](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_create_job). - """ - check_process_integrity(db, process_id, new_process=False) - ems_api_auth = HTTPBasicAuth( - settings.EMS_API_AUTH_USERNAME, settings.EMS_API_AUTH_PASSWORD.get_secret_value() - ) - try: - response = requests.get(f"{settings.EMS_API_URL}/dags/{process_id}", auth=ems_api_auth) - response.raise_for_status() - except requests.exceptions.HTTPError as e: - status_code_to_raise = fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR - detail_message = f"Failed to fetch DAG {process_id} due to an error." - if hasattr(e, "response"): - # If the exception has a response attribute, it's likely an HTTPError with more info - detail_message = f"Failed to fetch DAG {process_id}: {e.response.status_code} {e.response.reason}" - - raise HTTPException(status_code=status_code_to_raise, detail=detail_message) - - # TODO Validate that that the inputs and outputs conform to the schemas for inputs and outputs of the process - job_id = str(uuid.uuid4()) - logical_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ") - data = {"dag_run_id": job_id, "logical_date": logical_date, "conf": {**execute.model_dump()}} - try: - response = requests.post( - f"{settings.EMS_API_URL}/dags/{process_id}/dagRuns", json=data, auth=ems_api_auth - ) - response.raise_for_status() - check_job_integrity(db, job_id, new_job=True) - job = StatusInfo( - jobID=job_id, - processID=process_id, - type=Type2.process.value, - status=StatusCode.accepted.value, - ) - return crud.create_job(db, execute, job) - except requests.exceptions.RequestException as e: - status_code_to_raise = fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR - detail_message = f"Failed to start DAG run {job_id} with DAG {process_id} due to an error." - - if hasattr(e, "response"): - # If the exception has a response attribute, it's likely an HTTPError with more info - detail_message = f"Failed to start a DAG run {job_id} with DAG {process_id}: {e.response.status_code} {e.response.reason}" - - raise HTTPException(status_code=status_code_to_raise, detail=detail_message) - - -@app.get("/jobs/{job_id}", response_model=StatusInfo, summary="Retrieve the status of a job") -def status( - settings: Annotated[config.Settings, Depends(get_settings)], job_id: str, db: Session = Depends(get_db) -): - """ - Shows the status of a job. - - For more information, see [Section 7.12](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_retrieve_status_info). - """ - job = check_job_integrity(db, job_id, new_job=False) - job = StatusInfo.model_validate(job) - ems_api_auth = HTTPBasicAuth( - settings.EMS_API_AUTH_USERNAME, settings.EMS_API_AUTH_PASSWORD.get_secret_value() - ) - try: - response = requests.get( - f"{settings.EMS_API_URL}/dags/{job.processID}/dagRuns/{job.jobID}", - auth=ems_api_auth, - ) - response.raise_for_status() - except requests.exceptions.HTTPError as e: - status_code_to_raise = fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR - detail_message = f"Failed to fetch DAG run {job.jobID} for DAG {job.processID} due to an error." - if hasattr(e, "response"): - # If the exception has a response attribute, it's likely an HTTPError with more info - detail_message = f"Failed to fetch DAG run {job.jobID} for DAG {job.processID}: {e.response.status_code} {e.response.reason}" - - raise HTTPException(status_code=status_code_to_raise, detail=detail_message) - - execution_status_conversion_dict = { - "queued": StatusCode.accepted, - "running": StatusCode.running, - "success": StatusCode.successful, - "failed": StatusCode.failed, - } - data = response.json() - current_execution_status = execution_status_conversion_dict[data["state"]].value - if job.status != current_execution_status: - job.status = current_execution_status - updated = datetime.now() - job.updated = updated - - end_date_str = data.get("end_date", None) - if end_date_str: - end_date = datetime.fromisoformat(end_date_str) - job.finished = end_date - - return crud.update_job(db, job) - - -@app.delete( - "/jobs/{job_id}", response_model=StatusInfo, summary="Cancel a job execution, remove a finished job" -) -def dismiss( - settings: Annotated[config.Settings, Depends(get_settings)], job_id: str, db: Session = Depends(get_db) -): - """ - Cancel a job execution and remove it from the jobs list. - - For more information, see [Section 13](https://docs.ogc.org/is/18-062r2/18-062r2.html#Dismiss). - """ - job = check_job_integrity(db, job_id, new_job=False) - ems_api_auth = HTTPBasicAuth( - settings.EMS_API_AUTH_USERNAME, settings.EMS_API_AUTH_PASSWORD.get_secret_value() - ) - try: - # TODO also need to cancel all task instances - response = requests.delete( - f"{settings.EMS_API_URL}/dags/{job.processID}/dagRuns/{job.jobID}", - auth=ems_api_auth, - ) - response.raise_for_status() - except requests.exceptions.HTTPError as e: - status_code_to_raise = fastapi_status.HTTP_500_INTERNAL_SERVER_ERROR - detail_message = f"Failed to fetch DAG run {job.jobID} for DAG {job.processID} due to an error." - if hasattr(e, "response"): - # If the exception has a response attribute, it's likely an HTTPError with more info - detail_message = f"Failed to fetch DAG run {job.jobID} for DAG {job.processID}: {e.response.status_code} {e.response.reason}" - - raise HTTPException(status_code=status_code_to_raise, detail=detail_message) - - crud.delete_job(db, job) - job.status = StatusCode.dismissed.value - return job - - -@app.get("/jobs/{job_id}/results", response_model=Results, summary="Retrieve the result(s) of a job") -def results(job_id: str, db: Session = Depends(get_db)): - """ - Lists available results of a job. In case of a failure, lists exceptions instead. - - For more information, see [Section 7.13](https://docs.ogc.org/is/18-062r2/18-062r2.html#sc_retrieve_job_results). - """ - check_job_integrity(db, job_id, new_job=False) - return crud.get_results(db, job_id) diff --git a/app-cp/redis.py b/app-cp/redis.py deleted file mode 100644 index 49851b3..0000000 --- a/app-cp/redis.py +++ /dev/null @@ -1,25 +0,0 @@ -from contextlib import contextmanager - -import redis -from redis.exceptions import LockError - - -class RedisLock: - def __init__(self, client=None, host="localhost", port=6379, db=0): - if client is None: - client = redis.Redis(host=host, port=port, db=db) - self.client = client - - @contextmanager - def lock(self, lock_id, timeout=10): - """Attempt to acquire a lock within the given timeout period.""" - lock = self.client.lock(lock_id, timeout=timeout) - acquired = lock.acquire(blocking=True, blocking_timeout=timeout) - try: - if acquired: - yield lock - else: - raise LockError(f"Could not acquire lock for ID {lock_id}") - finally: - if acquired: - lock.release() diff --git a/app-cp/schemas/__init__.py b/app-cp/schemas/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/app-cp/schemas/ogc_processes.py b/app-cp/schemas/ogc_processes.py deleted file mode 100644 index 99b104c..0000000 --- a/app-cp/schemas/ogc_processes.py +++ /dev/null @@ -1,608 +0,0 @@ -# generated by datamodel-codegen: -# filename: ogcapi-processes.yaml -# timestamp: 2024-03-19T22:13:14+00:00 - -from __future__ import annotations - -from datetime import datetime -from enum import Enum -from typing import Any, Dict, List, Optional, Set, Union - -from pydantic import AnyUrl, BaseModel, ConfigDict, Field, PositiveFloat, RootModel, confloat, conint - - -class BaseSchema(BaseModel): - model_config = ConfigDict(use_enum_values=True, from_attributes=True) - - -class ConfClasses(BaseSchema): - conformsTo: List[str] - - -class Link(BaseSchema): - href: str - rel: Optional[str] = Field(None, json_schema_extra={"example": "service"}) - type: Optional[str] = Field(None, json_schema_extra={"example": "application/json"}) - hreflang: Optional[str] = Field(None, json_schema_extra={"example": "en"}) - title: Optional[str] = None - - -class LandingPage(BaseSchema): - title: Optional[str] = Field(None, json_schema_extra={"example": "Example processing server"}) - description: Optional[str] = Field( - None, - json_schema_extra={"example": "Example server implementing the OGC API - Processes 1.0 Standard"}, - ) - attribution: Optional[str] = Field( - None, - description="The `attribution` should be short and intended for presentation to a user, for example, in a corner of a map. Parts of the text can be links to other resources if additional information is needed. The string can include HTML markup.", - title="attribution for the Processes API", - ) - links: List[Link] - - -class Exception(BaseSchema): - model_config = ConfigDict(extra="allow") - - type: str - title: Optional[str] = None - status: Optional[int] = None - detail: Optional[str] = None - instance: Optional[str] = None - - -class Crs(Enum): - http___www_opengis_net_def_crs_OGC_1_3_CRS84 = "http://www.opengis.net/def/crs/OGC/1.3/CRS84" - http___www_opengis_net_def_crs_OGC_0_CRS84h = "http://www.opengis.net/def/crs/OGC/0/CRS84h" - - -class GridItem(BaseSchema): - coordinates: Optional[List[Union[str, float]]] = Field( - None, - description="List of coordinates along the dimension for which data organized as an irregular grid in the collection is available\n(e.g., 2, 10, 80, 100).", - json_schema_extra={"example": [2, 10, 80, 100]}, - min_length=1, - ) - cellsCount: Optional[int] = Field( - None, - description="Number of samples available along the dimension for data organized as a regular grid.\nFor values representing the whole area of contiguous cells spanning _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_.\nFor values representing infinitely small point cells spaced by _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_ + 1.", - json_schema_extra={"example": 50}, - ) - resolution: Optional[Union[str, float]] = Field( - None, - description="Resolution of regularly gridded data along the dimension in the collection", - json_schema_extra={"example": 0.0006866455078}, - ) - - -class Spatial(BaseSchema): - bbox: Optional[List[List[float]]] = Field( - None, - description="One or more bounding boxes that describe the spatial extent of the dataset.\nIn the Core only a single bounding box is supported.\n\nExtensions may support additional areas.\nThe first bounding box describes the overall spatial\nextent of the data. All subsequent bounding boxes describe\nmore precise bounding boxes, e.g., to identify clusters of data.\nClients only interested in the overall spatial extent will\nonly need to access the first item in each array.", - min_length=1, - ) - crs: Optional[Crs] = Field( - "http://www.opengis.net/def/crs/OGC/1.3/CRS84", - description="Coordinate reference system of the coordinates in the spatial extent\n(property `bbox`). The default reference system is WGS 84 longitude/latitude.\nIn the Core the only other supported coordinate reference system is\nWGS 84 longitude/latitude/ellipsoidal height for coordinates with height.\nExtensions may support additional coordinate reference systems and add\nadditional enum values.", - ) - grid: Optional[List[GridItem]] = Field( - None, - description="Provides information about the limited availability of data within the collection organized\nas a grid (regular or irregular) along each spatial dimension.", - max_length=3, - min_length=2, - ) - - -class IntervalItem(RootModel): - root: List[Any] = Field( - ..., - description="Begin and end times of the time interval. The timestamps are in the\ntemporal coordinate reference system specified in `trs`. By default\nthis is the Gregorian calendar.\n\nThe value `null` for start or end time is supported and indicates a half-bounded time interval.", - json_schema_extra={"example": ["2011-11-11T12:22:11Z", None]}, - ) - - -class Trs(Enum): - http___www_opengis_net_def_uom_ISO_8601_0_Gregorian = ( - "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian" - ) - - -class Grid(BaseSchema): - coordinates: Optional[List[str]] = Field( - None, - description='List of coordinates along the temporal dimension for which data organized as an irregular grid in the collection is available\n(e.g., "2017-11-14T09:00Z","2017-11-14T12:00Z","2017-11-14T15:00Z","2017-11-14T18:00Z","2017-11-14T21:00Z").', - json_schema_extra={"example": [["2020-11-12T12:15Z", "2020-11-12T12:30Z", "2020-11-12T12:45Z"]]}, - min_length=1, - ) - cellsCount: Optional[int] = Field( - None, - description="Number of samples available along the temporal dimension for data organized as a regular grid.\nFor values representing the whole area of contiguous cells spanning _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_.\nFor values representing infinitely small point cells spaced by _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_ + 1.", - json_schema_extra={"example": 50}, - ) - resolution: Optional[Union[str, float]] = Field( - None, - description="Resolution of regularly gridded data along the temporal dimension in the collection", - json_schema_extra={"example": "PT1H"}, - ) - - -class Temporal(BaseSchema): - interval: Optional[List[IntervalItem]] = Field( - None, - description="One or more time intervals that describe the temporal extent of the dataset.\nIn the Core only a single time interval is supported.\n\nExtensions may support multiple intervals.\nThe first time interval describes the overall\ntemporal extent of the data. All subsequent time intervals describe\nmore precise time intervals, e.g., to identify clusters of data.\nClients only interested in the overall extent will only need\nto access the first item in each array.", - min_length=1, - ) - trs: Optional[Trs] = Field( - "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian", - description="Coordinate reference system of the coordinates in the temporal extent\n(property `interval`). The default reference system is the Gregorian calendar.\nIn the Core this is the only supported temporal coordinate reference system.\nExtensions may support additional temporal coordinate reference systems and add\nadditional enum values.", - ) - grid: Optional[Grid] = Field( - None, - description="Provides information about the limited availability of data within the collection organized as a grid (regular or irregular) along the temporal dimension.", - ) - - -class Extent(BaseSchema): - spatial: Optional[Spatial] = Field(None, description="The spatial extent of the data in the collection.") - temporal: Optional[Temporal] = Field( - None, description="The temporal extent of the features in the collection." - ) - - -class DataType1(Enum): - map = "map" - vector = "vector" - coverage = "coverage" - - -class DataType(RootModel): - root: Union[str, DataType1] - - -class Crs2(BaseSchema): - uri: AnyUrl = Field(..., description="Reference to one coordinate reference system (CRS)") - - -class Wkt(BaseSchema): - pass - - -class Crs3(BaseSchema): - wkt: Wkt - - -class Crs4(BaseSchema): - referenceSystem: Dict[str, Any] = Field( - ..., - description="A reference system data structure as defined in the MD_ReferenceSystem of the ISO 19115", - ) - - -class CrsModel(RootModel): - root: Union[str, Union[Crs2, Crs3, Crs4]] = Field(..., title="CRS") - - -class TimeStamp(RootModel): - root: datetime = Field( - ..., - description="This property indicates the time and date when the response was generated using RFC 3339 notation.", - json_schema_extra={"example": "2017-08-17T08:05:32Z"}, - ) - - -class NumberReturned(RootModel): - root: conint(ge=0) = Field( - ..., - description='The number of features in the feature collection.\nA server may omit this information in a response, if the information\nabout the number of features is not known or difficult to compute.\nIf the value is provided, the value shall be identical to the number\nof items in the "features" array.', - json_schema_extra={"example": 10}, - ) - - -class NumberMatched(RootModel): - root: conint(ge=0) = Field( - ..., - description="The number of features of the feature type that match the selection\nparameters like `bbox`.", - json_schema_extra={"example": 127}, - ) - - -class Type(Enum): - enum = "enum" - - -class Enumeration(BaseSchema): - type: Type - enum: List[str] - - -class ProcessesList(Enum): - RenderMap = "RenderMap" - ElevationContours = "ElevationContours" - OSMERE = "OSMERE" - - -class Metadata1(Link): - role: Optional[str] = None - - -class Metadata2(BaseSchema): - role: Optional[str] = None - title: Optional[str] = None - lang: Optional[str] = None - value: Optional[Union[str, Dict[str, Any]]] = None - - -class Metadata(RootModel): - root: Union[Metadata1, Metadata2] - - -class JobControlOptions(Enum): - sync_execute = "sync-execute" - async_execute = "async-execute" - dismiss = "dismiss" - - -class ValuePassingEnum(Enum): - byValue = "byValue" - byReference = "byReference" - - -class MaxOccurs(Enum): - unbounded = "unbounded" - - -class Type1(Enum): - array = "array" - boolean = "boolean" - integer = "integer" - number = "number" - object = "object" - string = "string" - - -class Reference(BaseSchema): - model_config = ConfigDict(extra="forbid") - - field_ref: str = Field(..., alias="$ref") - - -class Type2(Enum): - process = "process" - - -class StatusCode(Enum): - accepted = "accepted" - running = "running" - successful = "successful" - failed = "failed" - dismissed = "dismissed" - - -class Crs5(Enum): - http___www_opengis_net_def_crs_OGC_1_3_CRS84 = "http://www.opengis.net/def/crs/OGC/1.3/CRS84" - http___www_opengis_net_def_crs_OGC_0_CRS84h = "http://www.opengis.net/def/crs/OGC/0/CRS84h" - - -class Bbox(BaseSchema): - bbox: List[float] - crs: Optional[Union[Crs5, AnyUrl]] = "http://www.opengis.net/def/crs/OGC/1.3/CRS84" - - -class BinaryInputValue(RootModel): - root: str - - -class InputValueNoObject(RootModel): - root: Union[str, float, int, bool, List, BinaryInputValue, Bbox] - - -class Format(BaseSchema): - mediaType: Optional[str] = None - encoding: Optional[str] = None - schema_: Optional[Union[str, Dict[str, Any]]] = Field(None, alias="schema") - - -class InputValue(RootModel): - root: Union[InputValueNoObject, Dict[str, Any]] - - -class Output(BaseSchema): - format: Optional[Format] = None - - -class Subscriber(BaseSchema): - successUri: AnyUrl - inProgressUri: Optional[AnyUrl] = None - failedUri: Optional[AnyUrl] = None - - -class Results(RootModel): - root: Any - - -class Type3(Enum): - docker = "docker" - oci = "oci" - - -class Deployment(Enum): - local = "local" - remote = "remote" - hpc = "hpc" - cloud = "cloud" - - -class Config(BaseSchema): - model_config = ConfigDict(extra="allow") - - cpuMin: Optional[confloat(ge=1.0)] = Field( - None, description="Minimum number of CPUs required to run the process (unit is CPU core)." - ) - cpuMax: Optional[float] = Field( - None, description="Maximum number of CPU dedicated to the process (unit is CPU core)" - ) - memoryMin: Optional[float] = Field( - None, description="Minimum RAM memory required to run the application (unit is GB)" - ) - memoryMax: Optional[float] = Field( - None, description="Maximum RAM memory dedicated to the application (unit is GB)" - ) - storageTempMin: Optional[float] = Field( - None, description="Minimum required temporary storage size (unit is GB)" - ) - storageOutputsMin: Optional[float] = Field( - None, description="Minimum required output storage size (unit is GB)" - ) - jobTimeout: Optional[float] = Field(None, description="Timeout delay for a job execution (in seconds)") - - -class ExecutionUnit(BaseSchema): - model_config = ConfigDict(extra="allow") - - type: Type3 = Field(..., description="Type of execution unit.") - image: str = Field(..., description="Container image reference for the execution unit.") - deployment: Optional[Deployment] = Field( - None, description="Deployment information for the execution unit." - ) - config: Optional[Config] = Field( - None, description="Hardware requirements and configuration properties for executing the process." - ) - - -class ExtentUad(Extent): - pass - - -class DescriptionType(BaseSchema): - title: Optional[str] = None - description: Optional[str] = None - keywords: Optional[List[str]] = None - metadata: Optional[List[Metadata]] = None - - -class StatusInfo(BaseSchema): - processID: Optional[str] = None - type: Type2 - jobID: str - status: StatusCode - message: Optional[str] = None - exception: Optional[Exception] = None - created: Optional[datetime] = None - started: Optional[datetime] = None - finished: Optional[datetime] = None - updated: Optional[datetime] = None - progress: Optional[conint(ge=0, le=100)] = None - links: Optional[List[Link]] = None - - -class BboxProcesses(RootModel): - root: Bbox - - -class Execute(BaseSchema): - inputs: Optional[Any] = None - outputs: Optional[Any] = None - subscriber: Optional[Subscriber] = None - - -class QualifiedInputValue(Format): - value: InputValue - - -class OgcapppkgArray(RootModel): - root: List[Union[ExecutionUnit, Link, QualifiedInputValue]] - - -class CollectionInfo(BaseSchema): - id: str = Field( - ..., - description="identifier of the collection used, for example, in URIs", - json_schema_extra={"example": "dem"}, - ) - title: Optional[str] = Field( - None, - description="human readable title of the collection", - json_schema_extra={"example": "Digital Elevation Model"}, - ) - description: Optional[str] = Field( - None, - description="a description of the data in the collection", - json_schema_extra={"example": "A Digital Elevation Model."}, - ) - links: List[Link] = Field( - ..., - json_schema_extra={ - "example": [ - { - "href": "http://data.example.org/collections/dem?f=json", - "rel": "self", - "type": "application/json", - "title": "Digital Elevation Model", - }, - { - "href": "http://data.example.org/collections/dem?f=html", - "rel": "alternate", - "type": "application/json", - "title": "Digital Elevation Model", - }, - { - "href": "http://data.example.org/collections/dem/coverage", - "rel": "coverage", - "type": "image/tiff; application=geotiff", - "title": "Digital Elevation Model", - }, - { - "href": "http://data.example.org/collections/dem/coverage/domainset", - "rel": "domainset", - "type": "application/json", - "title": "Digital Elevation Model", - }, - { - "href": "http://data.example.org/collections/dem/coverage/rangetype", - "rel": "rangetype", - "type": "application/json", - "title": "Digital Elevation Model", - }, - { - "href": "http://data.example.org/collections/dem/coverage/metadata", - "rel": "metadata", - "type": "application/json", - "title": "Digital Elevation Model", - }, - ] - }, - ) - extent: Optional[ExtentUad] = None - itemType: Optional[str] = Field( - "unknown", - description="indicator about the type of the items in the collection if the collection has an accessible /collections/{collectionId}/items endpoint", - ) - crs: Optional[List[str]] = Field( - ["http://www.opengis.net/def/crs/OGC/1.3/CRS84"], - description="the list of coordinate reference systems supported by the API; the first item is the default coordinate reference system", - json_schema_extra={ - "example": [ - "http://www.opengis.net/def/crs/OGC/1.3/CRS84", - "http://www.opengis.net/def/crs/EPSG/0/4326", - ] - }, - ) - dataType: Optional[DataType] = None - geometryDimension: Optional[conint(ge=0, le=3)] = Field( - None, - description="The geometry dimension of the features shown in this layer (0: points, 1: curves, 2: surfaces, 3: solids), unspecified: mixed or unknown", - ) - minScaleDenominator: Optional[float] = Field( - None, description="Minimum scale denominator for usage of the collection" - ) - maxScaleDenominator: Optional[float] = Field( - None, description="Maximum scale denominator for usage of the collection" - ) - minCellSize: Optional[float] = Field(None, description="Minimum cell size for usage of the collection") - maxCellSize: Optional[float] = Field(None, description="Maximum cell size for usage of the collection") - - -class ProcessSummary(DescriptionType): - id: str - version: str - jobControlOptions: Optional[List[JobControlOptions]] = None - links: Optional[List[Link]] = None - - -class Process(ProcessSummary): - inputs: Optional[List[InputValue]] = None - outputs: Optional[List[InputValue]] = None - - -class ProcessList(BaseSchema): - processes: List[ProcessSummary] - links: List[Link] - - -class JobList(BaseSchema): - jobs: List[StatusInfo] - links: List[Link] - - -class InlineOrRefData(RootModel): - root: Union[InputValueNoObject, QualifiedInputValue, Link] - - -class Ogcapppkg(BaseSchema): - processDescription: Optional[Process] = None - executionUnit: Union[ExecutionUnit, Link, QualifiedInputValue, OgcapppkgArray] - - -class StaticIndicator(ProcessSummary): - mutable: Optional[bool] = True - - -class Collections(BaseSchema): - links: List[Link] - timeStamp: Optional[datetime] = None - numberMatched: Optional[conint(ge=0)] = Field(None, json_schema_extra={"example": 1}) - numberReturned: Optional[conint(ge=0)] = Field(None, json_schema_extra={"example": 1}) - collections: List[CollectionInfo] - - -class Input(RootModel): - root: Union[InlineOrRefData, List[InlineOrRefData]] - - -class InputDescription(DescriptionType): - valuePassing: Optional[List[ValuePassingEnum]] = ["byValue", "byReference"] - minOccurs: Optional[int] = 1 - maxOccurs: Optional[Union[int, MaxOccurs]] = None - schema_: Schema = Field(..., alias="schema") - - -class Schema1(BaseSchema): - model_config = ConfigDict(extra="forbid") - - title: Optional[str] = None - multipleOf: Optional[PositiveFloat] = None - maximum: Optional[float] = None - exclusiveMaximum: Optional[bool] = False - minimum: Optional[float] = None - exclusiveMinimum: Optional[bool] = False - maxLength: Optional[conint(ge=0)] = None - minLength: Optional[conint(ge=0)] = 0 - pattern: Optional[str] = None - maxItems: Optional[conint(ge=0)] = None - minItems: Optional[conint(ge=0)] = 0 - uniqueItems: Optional[bool] = False - maxProperties: Optional[conint(ge=0)] = None - minProperties: Optional[conint(ge=0)] = 0 - required: Optional[Set[str]] = Field(None, min_length=1) - enum: Optional[List] = Field(None, min_length=1) - type: Optional[Type1] = None - not_: Optional[Schema] = Field(None, alias="not") - allOf: Optional[List[Schema]] = None - oneOf: Optional[List[Schema]] = None - anyOf: Optional[List[Schema]] = None - items: Optional[Schema] = None - properties: Optional[Dict[str, Schema]] = None - additionalProperties: Optional[Union[Schema, bool]] = True - description: Optional[str] = None - format: Optional[str] = None - default: Optional[Any] = None - nullable: Optional[bool] = False - readOnly: Optional[bool] = False - writeOnly: Optional[bool] = False - example: Optional[Any] = None - deprecated: Optional[bool] = False - contentMediaType: Optional[str] = None - contentEncoding: Optional[str] = None - contentSchema: Optional[str] = None - - -class Schema(RootModel): - root: Union[Reference, Schema1] - - -class OutputDescription(DescriptionType): - schema_: Schema = Field(..., alias="schema") - - -InputDescription.model_rebuild() -Schema1.model_rebuild() diff --git a/app-cp/schemas/unity_sps.py b/app-cp/schemas/unity_sps.py deleted file mode 100644 index ee53374..0000000 --- a/app-cp/schemas/unity_sps.py +++ /dev/null @@ -1,7 +0,0 @@ -from pydantic import BaseModel - - -class HealthCheck(BaseModel): - """Response model to validate and return when performing a health check.""" - - status: str = "OK" diff --git a/app/src/openapi_server/database/crud.py b/app/src/openapi_server/database/crud.py index e5e04fd..4244ab3 100644 --- a/app/src/openapi_server/database/crud.py +++ b/app/src/openapi_server/database/crud.py @@ -1,4 +1,5 @@ from sqlalchemy.orm import Session + from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg from . import models diff --git a/app/src/openapi_server/impl/api_api.py b/app/src/openapi_server/impl/api_api.py index e6c02cc..998b854 100644 --- a/app/src/openapi_server/impl/api_api.py +++ b/app/src/openapi_server/impl/api_api.py @@ -1,4 +1,5 @@ from fastapi.openapi.utils import get_openapi + from unity_sps_ogc_processes_api.apis.api_api_base import BaseAPIApi from unity_sps_ogc_processes_api.models.enumeration import Enumeration diff --git a/app/src/openapi_server/impl/jobs_api.py b/app/src/openapi_server/impl/jobs_api.py index 700ecb0..2d7845d 100644 --- a/app/src/openapi_server/impl/jobs_api.py +++ b/app/src/openapi_server/impl/jobs_api.py @@ -3,15 +3,16 @@ import requests from fastapi import HTTPException, status -from openapi_server.config.config import Settings -from openapi_server.database import crud -from openapi_server.utils.redis import RedisLock from redis.exceptions import LockError # from jsonschema import ValidationError, validate from requests.auth import HTTPBasicAuth from sqlalchemy.orm import Session from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound + +from openapi_server.config.config import Settings +from openapi_server.database import crud +from openapi_server.utils.redis import RedisLock from unity_sps_ogc_processes_api.apis.jobs_api_base import BaseJobsApi from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData from unity_sps_ogc_processes_api.models.job_list import JobList diff --git a/app/src/unity_sps_ogc_processes_api/apis/api_api.py b/app/src/unity_sps_ogc_processes_api/apis/api_api.py index bef8770..1bbdbdb 100644 --- a/app/src/unity_sps_ogc_processes_api/apis/api_api.py +++ b/app/src/unity_sps_ogc_processes_api/apis/api_api.py @@ -3,8 +3,9 @@ import importlib import pkgutil -import openapi_server.impl from fastapi import APIRouter, Query + +import openapi_server.impl from unity_sps_ogc_processes_api.apis.api_api_base import BaseAPIApi from unity_sps_ogc_processes_api.models.enumeration import Enumeration from unity_sps_ogc_processes_api.models.exception import Exception diff --git a/app/src/unity_sps_ogc_processes_api/apis/conformance_api.py b/app/src/unity_sps_ogc_processes_api/apis/conformance_api.py index b1470ba..2aa2bc7 100644 --- a/app/src/unity_sps_ogc_processes_api/apis/conformance_api.py +++ b/app/src/unity_sps_ogc_processes_api/apis/conformance_api.py @@ -3,8 +3,9 @@ import importlib import pkgutil -import openapi_server.impl from fastapi import APIRouter, Query + +import openapi_server.impl from unity_sps_ogc_processes_api.apis.conformance_api_base import BaseConformanceApi from unity_sps_ogc_processes_api.models.conf_classes import ConfClasses from unity_sps_ogc_processes_api.models.exception import Exception diff --git a/app/src/unity_sps_ogc_processes_api/apis/dru_api.py b/app/src/unity_sps_ogc_processes_api/apis/dru_api.py index a231574..deb99f7 100644 --- a/app/src/unity_sps_ogc_processes_api/apis/dru_api.py +++ b/app/src/unity_sps_ogc_processes_api/apis/dru_api.py @@ -3,13 +3,18 @@ import importlib import pkgutil -import openapi_server.impl from fastapi import APIRouter, Body, Depends, Path, Query +from sqlalchemy.orm import Session + +import openapi_server.impl from openapi_server.config.config import Settings from openapi_server.utils.redis import RedisLock -from sqlalchemy.orm import Session from unity_sps_ogc_processes_api.apis.dru_api_base import BaseDRUApi -from unity_sps_ogc_processes_api.dependencies import get_db, get_redis_locking_client, get_settings +from unity_sps_ogc_processes_api.dependencies import ( + get_db, + get_redis_locking_client, + get_settings, +) from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg diff --git a/app/src/unity_sps_ogc_processes_api/apis/health_api.py b/app/src/unity_sps_ogc_processes_api/apis/health_api.py index 6df354f..ce392cf 100644 --- a/app/src/unity_sps_ogc_processes_api/apis/health_api.py +++ b/app/src/unity_sps_ogc_processes_api/apis/health_api.py @@ -3,8 +3,9 @@ import importlib import pkgutil -import openapi_server.impl from fastapi import APIRouter + +import openapi_server.impl from unity_sps_ogc_processes_api.apis.health_api_base import BaseHealthApi from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.health_check import HealthCheck diff --git a/app/src/unity_sps_ogc_processes_api/apis/jobs_api.py b/app/src/unity_sps_ogc_processes_api/apis/jobs_api.py index 75b82f3..2903053 100644 --- a/app/src/unity_sps_ogc_processes_api/apis/jobs_api.py +++ b/app/src/unity_sps_ogc_processes_api/apis/jobs_api.py @@ -4,13 +4,18 @@ import pkgutil from typing import Dict -import openapi_server.impl from fastapi import APIRouter, Depends, Header, Path +from sqlalchemy.orm import Session + +import openapi_server.impl from openapi_server.config.config import Settings from openapi_server.utils.redis import RedisLock -from sqlalchemy.orm import Session from unity_sps_ogc_processes_api.apis.jobs_api_base import BaseJobsApi -from unity_sps_ogc_processes_api.dependencies import get_db, get_redis_locking_client, get_settings +from unity_sps_ogc_processes_api.dependencies import ( + get_db, + get_redis_locking_client, + get_settings, +) from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.extra_models import TokenModel # noqa: F401 from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData diff --git a/app/src/unity_sps_ogc_processes_api/apis/landing_page_api.py b/app/src/unity_sps_ogc_processes_api/apis/landing_page_api.py index 2454329..b1e8e2c 100644 --- a/app/src/unity_sps_ogc_processes_api/apis/landing_page_api.py +++ b/app/src/unity_sps_ogc_processes_api/apis/landing_page_api.py @@ -3,8 +3,9 @@ import importlib import pkgutil -import openapi_server.impl from fastapi import APIRouter, Query + +import openapi_server.impl from unity_sps_ogc_processes_api.apis.landing_page_api_base import BaseLandingPageApi from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.landing_page import LandingPage diff --git a/app/src/unity_sps_ogc_processes_api/apis/processes_api.py b/app/src/unity_sps_ogc_processes_api/apis/processes_api.py index 8302da8..8354861 100644 --- a/app/src/unity_sps_ogc_processes_api/apis/processes_api.py +++ b/app/src/unity_sps_ogc_processes_api/apis/processes_api.py @@ -3,13 +3,18 @@ import importlib import pkgutil -import openapi_server.impl from fastapi import APIRouter, Body, Depends, Header, Path, Query +from sqlalchemy.orm import Session + +import openapi_server.impl from openapi_server.config.config import Settings from openapi_server.utils.redis import RedisLock -from sqlalchemy.orm import Session from unity_sps_ogc_processes_api.apis.processes_api_base import BaseProcessesApi -from unity_sps_ogc_processes_api.dependencies import get_db, get_redis_locking_client, get_settings +from unity_sps_ogc_processes_api.dependencies import ( + get_db, + get_redis_locking_client, + get_settings, +) from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows diff --git a/app/src/unity_sps_ogc_processes_api/main.py b/app/src/unity_sps_ogc_processes_api/main.py index b86882e..c917617 100644 --- a/app/src/unity_sps_ogc_processes_api/main.py +++ b/app/src/unity_sps_ogc_processes_api/main.py @@ -14,15 +14,24 @@ from fastapi import Depends, FastAPI + from openapi_server.database import engine, models from unity_sps_ogc_processes_api.apis.api_api import router as APIApiRouter -from unity_sps_ogc_processes_api.apis.conformance_api import router as ConformanceApiRouter +from unity_sps_ogc_processes_api.apis.conformance_api import ( + router as ConformanceApiRouter, +) from unity_sps_ogc_processes_api.apis.dru_api import router as DRUApiRouter from unity_sps_ogc_processes_api.apis.health_api import router as HealthApiRouter from unity_sps_ogc_processes_api.apis.jobs_api import router as JobsApiRouter -from unity_sps_ogc_processes_api.apis.landing_page_api import router as LandingPageApiRouter +from unity_sps_ogc_processes_api.apis.landing_page_api import ( + router as LandingPageApiRouter, +) from unity_sps_ogc_processes_api.apis.processes_api import router as ProcessesApiRouter -from unity_sps_ogc_processes_api.dependencies import get_db, get_redis_locking_client, get_settings +from unity_sps_ogc_processes_api.dependencies import ( + get_db, + get_redis_locking_client, + get_settings, +) # Create database tables models.Base.metadata.create_all(bind=engine) diff --git a/app/src/unity_sps_ogc_processes_api/models/bbox.py b/app/src/unity_sps_ogc_processes_api/models/bbox.py index f211f68..8a1d958 100644 --- a/app/src/unity_sps_ogc_processes_api/models/bbox.py +++ b/app/src/unity_sps_ogc_processes_api/models/bbox.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional, Union from pydantic import BaseModel, StrictFloat, StrictInt + from unity_sps_ogc_processes_api.models.bbox_def_crs import BboxDefCrs try: diff --git a/app/src/unity_sps_ogc_processes_api/models/bbox1.py b/app/src/unity_sps_ogc_processes_api/models/bbox1.py index 6c47853..0da5f62 100644 --- a/app/src/unity_sps_ogc_processes_api/models/bbox1.py +++ b/app/src/unity_sps_ogc_processes_api/models/bbox1.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional, Union from pydantic import BaseModel, StrictFloat, StrictInt + from unity_sps_ogc_processes_api.models.bbox_def_crs import BboxDefCrs try: diff --git a/app/src/unity_sps_ogc_processes_api/models/bbox_processes.py b/app/src/unity_sps_ogc_processes_api/models/bbox_processes.py index d0a45a8..ac1d79b 100644 --- a/app/src/unity_sps_ogc_processes_api/models/bbox_processes.py +++ b/app/src/unity_sps_ogc_processes_api/models/bbox_processes.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional, Union from pydantic import BaseModel, StrictFloat, StrictInt + from unity_sps_ogc_processes_api.models.bbox_def_crs import BboxDefCrs try: diff --git a/app/src/unity_sps_ogc_processes_api/models/collection_info.py b/app/src/unity_sps_ogc_processes_api/models/collection_info.py index af90b89..41e5f65 100644 --- a/app/src/unity_sps_ogc_processes_api/models/collection_info.py +++ b/app/src/unity_sps_ogc_processes_api/models/collection_info.py @@ -22,7 +22,10 @@ from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr from typing_extensions import Annotated -from unity_sps_ogc_processes_api.models.collection_info_data_type import CollectionInfoDataType + +from unity_sps_ogc_processes_api.models.collection_info_data_type import ( + CollectionInfoDataType, +) from unity_sps_ogc_processes_api.models.extent_uad import ExtentUad from unity_sps_ogc_processes_api.models.link import Link diff --git a/app/src/unity_sps_ogc_processes_api/models/collections.py b/app/src/unity_sps_ogc_processes_api/models/collections.py index d2d852b..2115561 100644 --- a/app/src/unity_sps_ogc_processes_api/models/collections.py +++ b/app/src/unity_sps_ogc_processes_api/models/collections.py @@ -23,6 +23,7 @@ from pydantic import BaseModel, Field from typing_extensions import Annotated + from unity_sps_ogc_processes_api.models.collection_info import CollectionInfo from unity_sps_ogc_processes_api.models.link import Link diff --git a/app/src/unity_sps_ogc_processes_api/models/crs.py b/app/src/unity_sps_ogc_processes_api/models/crs.py index 5dbcee6..068b6ec 100644 --- a/app/src/unity_sps_ogc_processes_api/models/crs.py +++ b/app/src/unity_sps_ogc_processes_api/models/crs.py @@ -22,6 +22,7 @@ from pydantic import BaseModel, Field, StrictStr, ValidationError, field_validator from typing_extensions import Literal + from unity_sps_ogc_processes_api.models.crs_one_of import CrsOneOf try: diff --git a/app/src/unity_sps_ogc_processes_api/models/crs_one_of.py b/app/src/unity_sps_ogc_processes_api/models/crs_one_of.py index b037cd3..858ae6c 100644 --- a/app/src/unity_sps_ogc_processes_api/models/crs_one_of.py +++ b/app/src/unity_sps_ogc_processes_api/models/crs_one_of.py @@ -22,6 +22,7 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal + from unity_sps_ogc_processes_api.models.crs_one_of_one_of import CrsOneOfOneOf from unity_sps_ogc_processes_api.models.crs_one_of_one_of1 import CrsOneOfOneOf1 from unity_sps_ogc_processes_api.models.crs_one_of_one_of2 import CrsOneOfOneOf2 diff --git a/app/src/unity_sps_ogc_processes_api/models/description_type.py b/app/src/unity_sps_ogc_processes_api/models/description_type.py index 62c1729..305fecf 100644 --- a/app/src/unity_sps_ogc_processes_api/models/description_type.py +++ b/app/src/unity_sps_ogc_processes_api/models/description_type.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, StrictStr + from unity_sps_ogc_processes_api.models.metadata import Metadata try: diff --git a/app/src/unity_sps_ogc_processes_api/models/execute.py b/app/src/unity_sps_ogc_processes_api/models/execute.py index 7218502..729152f 100644 --- a/app/src/unity_sps_ogc_processes_api/models/execute.py +++ b/app/src/unity_sps_ogc_processes_api/models/execute.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel + from unity_sps_ogc_processes_api.models.input import Input from unity_sps_ogc_processes_api.models.output import Output from unity_sps_ogc_processes_api.models.subscriber import Subscriber diff --git a/app/src/unity_sps_ogc_processes_api/models/execute200_response.py b/app/src/unity_sps_ogc_processes_api/models/execute200_response.py index 320b91f..fa2c7e6 100644 --- a/app/src/unity_sps_ogc_processes_api/models/execute200_response.py +++ b/app/src/unity_sps_ogc_processes_api/models/execute200_response.py @@ -19,6 +19,7 @@ from typing import Any, Dict, List, Union from pydantic import FilePath, RootModel, field_validator + from unity_sps_ogc_processes_api.models.bbox import Bbox from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.qualified_input_value import QualifiedInputValue diff --git a/app/src/unity_sps_ogc_processes_api/models/execute200_response1.py b/app/src/unity_sps_ogc_processes_api/models/execute200_response1.py index d802421..e7e2478 100644 --- a/app/src/unity_sps_ogc_processes_api/models/execute200_response1.py +++ b/app/src/unity_sps_ogc_processes_api/models/execute200_response1.py @@ -20,8 +20,16 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) from typing_extensions import Annotated + from unity_sps_ogc_processes_api.models.geo_json_feature import GeoJSONFeature try: diff --git a/app/src/unity_sps_ogc_processes_api/models/execute_workflows.py b/app/src/unity_sps_ogc_processes_api/models/execute_workflows.py index b526f16..087955f 100644 --- a/app/src/unity_sps_ogc_processes_api/models/execute_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/execute_workflows.py @@ -21,7 +21,10 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr -from unity_sps_ogc_processes_api.models.fields_modifiers_properties import FieldsModifiersProperties + +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( + FieldsModifiersProperties, +) from unity_sps_ogc_processes_api.models.input_workflows import InputWorkflows from unity_sps_ogc_processes_api.models.output_workflows import OutputWorkflows from unity_sps_ogc_processes_api.models.subscriber import Subscriber diff --git a/app/src/unity_sps_ogc_processes_api/models/execute_workflows1.py b/app/src/unity_sps_ogc_processes_api/models/execute_workflows1.py index c060c4e..2f66be7 100644 --- a/app/src/unity_sps_ogc_processes_api/models/execute_workflows1.py +++ b/app/src/unity_sps_ogc_processes_api/models/execute_workflows1.py @@ -21,7 +21,10 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr -from unity_sps_ogc_processes_api.models.fields_modifiers_properties import FieldsModifiersProperties + +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( + FieldsModifiersProperties, +) from unity_sps_ogc_processes_api.models.input_workflows1 import InputWorkflows1 from unity_sps_ogc_processes_api.models.output_workflows1 import OutputWorkflows1 from unity_sps_ogc_processes_api.models.subscriber import Subscriber diff --git a/app/src/unity_sps_ogc_processes_api/models/execution_unit.py b/app/src/unity_sps_ogc_processes_api/models/execution_unit.py index a333153..7ea86a4 100644 --- a/app/src/unity_sps_ogc_processes_api/models/execution_unit.py +++ b/app/src/unity_sps_ogc_processes_api/models/execution_unit.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr, field_validator + from unity_sps_ogc_processes_api.models.execution_unit_config import ExecutionUnitConfig try: diff --git a/app/src/unity_sps_ogc_processes_api/models/extent.py b/app/src/unity_sps_ogc_processes_api/models/extent.py index a335b27..12104a7 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel + from unity_sps_ogc_processes_api.models.extent_spatial import ExtentSpatial from unity_sps_ogc_processes_api.models.extent_temporal import ExtentTemporal diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_spatial.py b/app/src/unity_sps_ogc_processes_api/models/extent_spatial.py index 90d36c9..90f0ac4 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_spatial.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_spatial.py @@ -20,9 +20,19 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) from typing_extensions import Annotated -from unity_sps_ogc_processes_api.models.extent_spatial_grid_inner import ExtentSpatialGridInner + +from unity_sps_ogc_processes_api.models.extent_spatial_grid_inner import ( + ExtentSpatialGridInner, +) try: from typing import Self diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py index 8caf637..a35bd21 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py @@ -22,6 +22,7 @@ from pydantic import BaseModel, Field, StrictInt from typing_extensions import Annotated + from unity_sps_ogc_processes_api.models.extent_spatial_grid_inner_coordinates_inner import ( ExtentSpatialGridInnerCoordinatesInner, ) diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py index 3d0e20b..2c1def8 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py @@ -20,7 +20,14 @@ import re # noqa: F401 from typing import Dict, List, Optional, Union -from pydantic import BaseModel, StrictFloat, StrictInt, StrictStr, ValidationError, field_validator +from pydantic import ( + BaseModel, + StrictFloat, + StrictInt, + StrictStr, + ValidationError, + field_validator, +) from typing_extensions import Literal try: diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py index 51448a4..8286284 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py @@ -20,7 +20,14 @@ import re # noqa: F401 from typing import Dict, List, Optional, Union -from pydantic import BaseModel, StrictFloat, StrictInt, StrictStr, ValidationError, field_validator +from pydantic import ( + BaseModel, + StrictFloat, + StrictInt, + StrictStr, + ValidationError, + field_validator, +) from typing_extensions import Literal try: diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_temporal.py b/app/src/unity_sps_ogc_processes_api/models/extent_temporal.py index 77d4618..5d38380 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_temporal.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_temporal.py @@ -23,6 +23,7 @@ from pydantic import BaseModel, Field, StrictStr, field_validator from typing_extensions import Annotated + from unity_sps_ogc_processes_api.models.extent_temporal_grid import ExtentTemporalGrid try: diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py b/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py index ad1bad5..83b0821 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py @@ -22,7 +22,10 @@ from pydantic import BaseModel, Field, StrictInt, StrictStr from typing_extensions import Annotated -from unity_sps_ogc_processes_api.models.extent_temporal_grid_resolution import ExtentTemporalGridResolution + +from unity_sps_ogc_processes_api.models.extent_temporal_grid_resolution import ( + ExtentTemporalGridResolution, +) try: from typing import Self diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py b/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py index b08ed8e..2386d64 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py @@ -20,7 +20,14 @@ import re # noqa: F401 from typing import Dict, List, Optional, Union -from pydantic import BaseModel, StrictFloat, StrictInt, StrictStr, ValidationError, field_validator +from pydantic import ( + BaseModel, + StrictFloat, + StrictInt, + StrictStr, + ValidationError, + field_validator, +) from typing_extensions import Literal try: diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_uad.py b/app/src/unity_sps_ogc_processes_api/models/extent_uad.py index 60b7bda..6fad392 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_uad.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_uad.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel + from unity_sps_ogc_processes_api.models.extent_spatial import ExtentSpatial from unity_sps_ogc_processes_api.models.extent_temporal import ExtentTemporal diff --git a/app/src/unity_sps_ogc_processes_api/models/feature_collection.py b/app/src/unity_sps_ogc_processes_api/models/feature_collection.py index 75289b9..e07b601 100644 --- a/app/src/unity_sps_ogc_processes_api/models/feature_collection.py +++ b/app/src/unity_sps_ogc_processes_api/models/feature_collection.py @@ -20,8 +20,16 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) from typing_extensions import Annotated + from unity_sps_ogc_processes_api.models.geo_json_feature import GeoJSONFeature try: diff --git a/app/src/unity_sps_ogc_processes_api/models/fields_modifiers.py b/app/src/unity_sps_ogc_processes_api/models/fields_modifiers.py index 34abada..9b71705 100644 --- a/app/src/unity_sps_ogc_processes_api/models/fields_modifiers.py +++ b/app/src/unity_sps_ogc_processes_api/models/fields_modifiers.py @@ -21,7 +21,10 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr -from unity_sps_ogc_processes_api.models.fields_modifiers_properties import FieldsModifiersProperties + +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( + FieldsModifiersProperties, +) try: from typing import Self diff --git a/app/src/unity_sps_ogc_processes_api/models/format.py b/app/src/unity_sps_ogc_processes_api/models/format.py index ebe480c..c795faf 100644 --- a/app/src/unity_sps_ogc_processes_api/models/format.py +++ b/app/src/unity_sps_ogc_processes_api/models/format.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr + from unity_sps_ogc_processes_api.models.format_schema import FormatSchema try: diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_feature.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature.py index cd2e36a..544ff11 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_feature.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature.py @@ -20,9 +20,19 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) from typing_extensions import Annotated -from unity_sps_ogc_processes_api.models.geo_json_feature_geometry import GeoJSONFeatureGeometry + +from unity_sps_ogc_processes_api.models.geo_json_feature_geometry import ( + GeoJSONFeatureGeometry, +) from unity_sps_ogc_processes_api.models.geo_json_feature_id import GeoJSONFeatureId try: diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py index 68639ad..0a0799e 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py @@ -22,10 +22,15 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal + from unity_sps_ogc_processes_api.models.geo_json_line_string import GeoJSONLineString -from unity_sps_ogc_processes_api.models.geo_json_multi_line_string import GeoJSONMultiLineString +from unity_sps_ogc_processes_api.models.geo_json_multi_line_string import ( + GeoJSONMultiLineString, +) from unity_sps_ogc_processes_api.models.geo_json_multi_point import GeoJSONMultiPoint -from unity_sps_ogc_processes_api.models.geo_json_multi_polygon import GeoJSONMultiPolygon +from unity_sps_ogc_processes_api.models.geo_json_multi_polygon import ( + GeoJSONMultiPolygon, +) from unity_sps_ogc_processes_api.models.geo_json_point import GeoJSONPoint from unity_sps_ogc_processes_api.models.geo_json_polygon import GeoJSONPolygon diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py index b1ece61..7407599 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py @@ -20,7 +20,14 @@ import re # noqa: F401 from typing import Dict, List, Optional, Union -from pydantic import BaseModel, StrictFloat, StrictInt, StrictStr, ValidationError, field_validator +from pydantic import ( + BaseModel, + StrictFloat, + StrictInt, + StrictStr, + ValidationError, + field_validator, +) from typing_extensions import Literal try: diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py index f0a7119..02e4d4d 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py @@ -20,7 +20,14 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) from typing_extensions import Annotated try: diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py index cca0b2b..726ca9f 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py @@ -20,7 +20,14 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) from typing_extensions import Annotated try: diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py index bea5843..da59f08 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py @@ -20,7 +20,14 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) from typing_extensions import Annotated try: diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py index ebe5b46..1bc084c 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py @@ -20,7 +20,14 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) from typing_extensions import Annotated try: diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_point.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_point.py index fa7869e..6683680 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_point.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_point.py @@ -20,7 +20,14 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) from typing_extensions import Annotated try: diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py index 5233ba1..d4d32ac 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py @@ -20,7 +20,14 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, field_validator +from pydantic import ( + BaseModel, + Field, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) from typing_extensions import Annotated try: diff --git a/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py index d4cca9d..87f931f 100644 --- a/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py +++ b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py @@ -20,6 +20,7 @@ from typing import Any, Dict, List, Union from pydantic import RootModel, model_validator + from unity_sps_ogc_processes_api.models.bbox import Bbox from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.qualified_input_value import QualifiedInputValue diff --git a/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py index 08b49fb..6eb03a4 100644 --- a/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py +++ b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py @@ -22,9 +22,14 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal -from unity_sps_ogc_processes_api.models.input_value_no_object1 import InputValueNoObject1 + +from unity_sps_ogc_processes_api.models.input_value_no_object1 import ( + InputValueNoObject1, +) from unity_sps_ogc_processes_api.models.link import Link -from unity_sps_ogc_processes_api.models.qualified_input_value1 import QualifiedInputValue1 +from unity_sps_ogc_processes_api.models.qualified_input_value1 import ( + QualifiedInputValue1, +) try: from typing import Self diff --git a/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py index d2bc4ab..d44f580 100644 --- a/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py @@ -22,6 +22,7 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal + from unity_sps_ogc_processes_api.models.link import Link try: @@ -173,8 +174,12 @@ def to_str(self) -> str: return pprint.pformat(self.model_dump()) -from unity_sps_ogc_processes_api.models.input_value_no_object_workflows import InputValueNoObjectWorkflows -from unity_sps_ogc_processes_api.models.qualified_input_value_workflows import QualifiedInputValueWorkflows +from unity_sps_ogc_processes_api.models.input_value_no_object_workflows import ( + InputValueNoObjectWorkflows, +) +from unity_sps_ogc_processes_api.models.qualified_input_value_workflows import ( + QualifiedInputValueWorkflows, +) # TODO: Rewrite to not use raise_errors InlineOrRefDataWorkflows.model_rebuild(raise_errors=False) diff --git a/app/src/unity_sps_ogc_processes_api/models/input.py b/app/src/unity_sps_ogc_processes_api/models/input.py index b619a99..5a5ee1b 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input.py +++ b/app/src/unity_sps_ogc_processes_api/models/input.py @@ -5,9 +5,12 @@ from typing import Any, Dict, List, Union from pydantic import RootModel, model_validator + from unity_sps_ogc_processes_api.models.bbox1 import Bbox1 from unity_sps_ogc_processes_api.models.link import Link -from unity_sps_ogc_processes_api.models.qualified_input_value1 import QualifiedInputValue1 +from unity_sps_ogc_processes_api.models.qualified_input_value1 import ( + QualifiedInputValue1, +) class Input(RootModel): diff --git a/app/src/unity_sps_ogc_processes_api/models/input_collection.py b/app/src/unity_sps_ogc_processes_api/models/input_collection.py index 803ce6f..fa243d3 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_collection.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_collection.py @@ -21,7 +21,10 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr -from unity_sps_ogc_processes_api.models.fields_modifiers_properties import FieldsModifiersProperties + +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( + FieldsModifiersProperties, +) try: from typing import Self diff --git a/app/src/unity_sps_ogc_processes_api/models/input_description.py b/app/src/unity_sps_ogc_processes_api/models/input_description.py index 233062f..69e9b1b 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_description.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_description.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictInt, StrictStr, field_validator + from unity_sps_ogc_processes_api.models.input_description_all_of_max_occurs import ( InputDescriptionAllOfMaxOccurs, ) diff --git a/app/src/unity_sps_ogc_processes_api/models/input_parameterized.py b/app/src/unity_sps_ogc_processes_api/models/input_parameterized.py index aa22d1a..71b58f8 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_parameterized.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_parameterized.py @@ -21,7 +21,10 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr -from unity_sps_ogc_processes_api.models.fields_modifiers_properties import FieldsModifiersProperties + +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( + FieldsModifiersProperties, +) try: from typing import Self diff --git a/app/src/unity_sps_ogc_processes_api/models/input_process.py b/app/src/unity_sps_ogc_processes_api/models/input_process.py index a7f83bd..d641326 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_process.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_process.py @@ -21,7 +21,10 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr -from unity_sps_ogc_processes_api.models.fields_modifiers_properties import FieldsModifiersProperties + +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( + FieldsModifiersProperties, +) from unity_sps_ogc_processes_api.models.output_workflows1 import OutputWorkflows1 from unity_sps_ogc_processes_api.models.subscriber import Subscriber diff --git a/app/src/unity_sps_ogc_processes_api/models/input_value.py b/app/src/unity_sps_ogc_processes_api/models/input_value.py index 568a79a..c8d8a66 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_value.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value.py @@ -22,6 +22,7 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal + from unity_sps_ogc_processes_api.models.input_value_no_object import InputValueNoObject try: diff --git a/app/src/unity_sps_ogc_processes_api/models/input_value1.py b/app/src/unity_sps_ogc_processes_api/models/input_value1.py index 7c070f4..bccdf85 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_value1.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value1.py @@ -22,7 +22,10 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal -from unity_sps_ogc_processes_api.models.input_value_no_object1 import InputValueNoObject1 + +from unity_sps_ogc_processes_api.models.input_value_no_object1 import ( + InputValueNoObject1, +) try: from typing import Self diff --git a/app/src/unity_sps_ogc_processes_api/models/input_value_no_object.py b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object.py index c11dfde..e327c7d 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_value_no_object.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object.py @@ -31,6 +31,7 @@ field_validator, ) from typing_extensions import Literal + from unity_sps_ogc_processes_api.models.bbox import Bbox try: diff --git a/app/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py index 5de380e..575f620 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py @@ -31,6 +31,7 @@ field_validator, ) from typing_extensions import Literal + from unity_sps_ogc_processes_api.models.bbox1 import Bbox1 try: diff --git a/app/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py index 2bff507..468c719 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py @@ -31,6 +31,7 @@ field_validator, ) from typing_extensions import Literal + from unity_sps_ogc_processes_api.models.bbox1 import Bbox1 from unity_sps_ogc_processes_api.models.input_collection import InputCollection from unity_sps_ogc_processes_api.models.input_parameterized import InputParameterized diff --git a/app/src/unity_sps_ogc_processes_api/models/input_value_workflows.py b/app/src/unity_sps_ogc_processes_api/models/input_value_workflows.py index 0f64ec0..4b83a74 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_value_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value_workflows.py @@ -159,7 +159,9 @@ def to_str(self) -> str: return pprint.pformat(self.model_dump()) -from unity_sps_ogc_processes_api.models.input_value_no_object_workflows import InputValueNoObjectWorkflows +from unity_sps_ogc_processes_api.models.input_value_no_object_workflows import ( + InputValueNoObjectWorkflows, +) # TODO: Rewrite to not use raise_errors InputValueWorkflows.model_rebuild(raise_errors=False) diff --git a/app/src/unity_sps_ogc_processes_api/models/input_workflows.py b/app/src/unity_sps_ogc_processes_api/models/input_workflows.py index a5052e0..65bb11b 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_workflows.py @@ -20,12 +20,15 @@ from typing import Any, Dict, List, Union from pydantic import RootModel, ValidationError, model_validator + from unity_sps_ogc_processes_api.models.bbox1 import Bbox1 from unity_sps_ogc_processes_api.models.input_collection import InputCollection from unity_sps_ogc_processes_api.models.input_parameterized import InputParameterized from unity_sps_ogc_processes_api.models.input_process import InputProcess from unity_sps_ogc_processes_api.models.link import Link -from unity_sps_ogc_processes_api.models.qualified_input_value_workflows import QualifiedInputValueWorkflows +from unity_sps_ogc_processes_api.models.qualified_input_value_workflows import ( + QualifiedInputValueWorkflows, +) class InputWorkflows(RootModel): diff --git a/app/src/unity_sps_ogc_processes_api/models/input_workflows1.py b/app/src/unity_sps_ogc_processes_api/models/input_workflows1.py index c58823c..d5ebcf5 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_workflows1.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_workflows1.py @@ -162,7 +162,9 @@ def to_str(self) -> str: return pprint.pformat(self.model_dump()) -from unity_sps_ogc_processes_api.models.inline_or_ref_data_workflows import InlineOrRefDataWorkflows +from unity_sps_ogc_processes_api.models.inline_or_ref_data_workflows import ( + InlineOrRefDataWorkflows, +) # TODO: Rewrite to not use raise_errors InputWorkflows1.model_rebuild(raise_errors=False) diff --git a/app/src/unity_sps_ogc_processes_api/models/job_list.py b/app/src/unity_sps_ogc_processes_api/models/job_list.py index 5dbffae..9fe2f2f 100644 --- a/app/src/unity_sps_ogc_processes_api/models/job_list.py +++ b/app/src/unity_sps_ogc_processes_api/models/job_list.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List from pydantic import BaseModel + from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.status_info import StatusInfo diff --git a/app/src/unity_sps_ogc_processes_api/models/landing_page.py b/app/src/unity_sps_ogc_processes_api/models/landing_page.py index 3b3805c..2fffd7f 100644 --- a/app/src/unity_sps_ogc_processes_api/models/landing_page.py +++ b/app/src/unity_sps_ogc_processes_api/models/landing_page.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr + from unity_sps_ogc_processes_api.models.link import Link try: diff --git a/app/src/unity_sps_ogc_processes_api/models/metadata.py b/app/src/unity_sps_ogc_processes_api/models/metadata.py index de30ee9..963f702 100644 --- a/app/src/unity_sps_ogc_processes_api/models/metadata.py +++ b/app/src/unity_sps_ogc_processes_api/models/metadata.py @@ -4,6 +4,7 @@ from typing import Any, Dict, Union from pydantic import RootModel, ValidationError, model_validator + from unity_sps_ogc_processes_api.models.metadata_one_of import MetadataOneOf from unity_sps_ogc_processes_api.models.metadata_one_of1 import MetadataOneOf1 diff --git a/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py b/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py index d54cd8f..df985c6 100644 --- a/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py +++ b/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py @@ -21,7 +21,10 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, StrictStr -from unity_sps_ogc_processes_api.models.metadata_one_of1_value import MetadataOneOf1Value + +from unity_sps_ogc_processes_api.models.metadata_one_of1_value import ( + MetadataOneOf1Value, +) try: from typing import Self diff --git a/app/src/unity_sps_ogc_processes_api/models/model_schema.py b/app/src/unity_sps_ogc_processes_api/models/model_schema.py index b8834e8..a9e7f42 100644 --- a/app/src/unity_sps_ogc_processes_api/models/model_schema.py +++ b/app/src/unity_sps_ogc_processes_api/models/model_schema.py @@ -20,6 +20,7 @@ from typing import Any, Dict, Union from pydantic import RootModel, ValidationError, model_validator + from unity_sps_ogc_processes_api.models.reference import Reference from unity_sps_ogc_processes_api.models.schema_one_of import SchemaOneOf diff --git a/app/src/unity_sps_ogc_processes_api/models/ogcapppkg.py b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg.py index f1265e1..5c5d2c3 100644 --- a/app/src/unity_sps_ogc_processes_api/models/ogcapppkg.py +++ b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg.py @@ -20,7 +20,10 @@ from typing import Any, ClassVar, Dict, List from pydantic import BaseModel, Field -from unity_sps_ogc_processes_api.models.ogcapppkg_execution_unit import OgcapppkgExecutionUnit + +from unity_sps_ogc_processes_api.models.ogcapppkg_execution_unit import ( + OgcapppkgExecutionUnit, +) from unity_sps_ogc_processes_api.models.process import Process try: diff --git a/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py index 4d34690..44859f2 100644 --- a/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py +++ b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py @@ -22,6 +22,7 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal + from unity_sps_ogc_processes_api.models.execution_unit import ExecutionUnit from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.qualified_input_value import QualifiedInputValue diff --git a/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py index 122bd01..2448633 100644 --- a/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py +++ b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py @@ -20,6 +20,7 @@ from typing import Any, Dict, List, Union from pydantic import RootModel, ValidationError, model_validator + from unity_sps_ogc_processes_api.models.execution_unit import ExecutionUnit from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.qualified_input_value import QualifiedInputValue diff --git a/app/src/unity_sps_ogc_processes_api/models/output.py b/app/src/unity_sps_ogc_processes_api/models/output.py index 735b4df..a93851a 100644 --- a/app/src/unity_sps_ogc_processes_api/models/output.py +++ b/app/src/unity_sps_ogc_processes_api/models/output.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel + from unity_sps_ogc_processes_api.models.format import Format try: diff --git a/app/src/unity_sps_ogc_processes_api/models/output_description.py b/app/src/unity_sps_ogc_processes_api/models/output_description.py index 5e1e5d8..4b81393 100644 --- a/app/src/unity_sps_ogc_processes_api/models/output_description.py +++ b/app/src/unity_sps_ogc_processes_api/models/output_description.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr + from unity_sps_ogc_processes_api.models.metadata import Metadata from unity_sps_ogc_processes_api.models.model_schema import ModelSchema diff --git a/app/src/unity_sps_ogc_processes_api/models/output_workflows.py b/app/src/unity_sps_ogc_processes_api/models/output_workflows.py index a0038b6..9db9b75 100644 --- a/app/src/unity_sps_ogc_processes_api/models/output_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/output_workflows.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr + from unity_sps_ogc_processes_api.models.format import Format try: diff --git a/app/src/unity_sps_ogc_processes_api/models/output_workflows1.py b/app/src/unity_sps_ogc_processes_api/models/output_workflows1.py index dccfcaf..6720f17 100644 --- a/app/src/unity_sps_ogc_processes_api/models/output_workflows1.py +++ b/app/src/unity_sps_ogc_processes_api/models/output_workflows1.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr + from unity_sps_ogc_processes_api.models.format import Format try: diff --git a/app/src/unity_sps_ogc_processes_api/models/process.py b/app/src/unity_sps_ogc_processes_api/models/process.py index d6b4b8b..0095243 100644 --- a/app/src/unity_sps_ogc_processes_api/models/process.py +++ b/app/src/unity_sps_ogc_processes_api/models/process.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr + from unity_sps_ogc_processes_api.models.input_description import InputDescription from unity_sps_ogc_processes_api.models.job_control_options import JobControlOptions from unity_sps_ogc_processes_api.models.link import Link diff --git a/app/src/unity_sps_ogc_processes_api/models/process_list.py b/app/src/unity_sps_ogc_processes_api/models/process_list.py index 6657aa8..d7ac593 100644 --- a/app/src/unity_sps_ogc_processes_api/models/process_list.py +++ b/app/src/unity_sps_ogc_processes_api/models/process_list.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List from pydantic import BaseModel + from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.process_summary import ProcessSummary diff --git a/app/src/unity_sps_ogc_processes_api/models/process_summary.py b/app/src/unity_sps_ogc_processes_api/models/process_summary.py index 9d784d7..175d13c 100644 --- a/app/src/unity_sps_ogc_processes_api/models/process_summary.py +++ b/app/src/unity_sps_ogc_processes_api/models/process_summary.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr + from unity_sps_ogc_processes_api.models.job_control_options import JobControlOptions from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.metadata import Metadata diff --git a/app/src/unity_sps_ogc_processes_api/models/qualified_input_value.py b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value.py index 284ba54..d25f160 100644 --- a/app/src/unity_sps_ogc_processes_api/models/qualified_input_value.py +++ b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr + from unity_sps_ogc_processes_api.models.format_schema import FormatSchema from unity_sps_ogc_processes_api.models.input_value import InputValue diff --git a/app/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py index 063a825..d82bd76 100644 --- a/app/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py +++ b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr + from unity_sps_ogc_processes_api.models.format_schema import FormatSchema from unity_sps_ogc_processes_api.models.input_value1 import InputValue1 diff --git a/app/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py index 9308292..07feebd 100644 --- a/app/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py @@ -21,7 +21,10 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictStr -from unity_sps_ogc_processes_api.models.fields_modifiers_properties import FieldsModifiersProperties + +from unity_sps_ogc_processes_api.models.fields_modifiers_properties import ( + FieldsModifiersProperties, +) from unity_sps_ogc_processes_api.models.format_schema import FormatSchema try: diff --git a/app/src/unity_sps_ogc_processes_api/models/schema1.py b/app/src/unity_sps_ogc_processes_api/models/schema1.py index d225f46..2c7ab5c 100644 --- a/app/src/unity_sps_ogc_processes_api/models/schema1.py +++ b/app/src/unity_sps_ogc_processes_api/models/schema1.py @@ -22,6 +22,7 @@ from pydantic import BaseModel, ValidationError, field_validator from typing_extensions import Literal + from unity_sps_ogc_processes_api.models.reference import Reference try: diff --git a/app/src/unity_sps_ogc_processes_api/models/schema_one_of.py b/app/src/unity_sps_ogc_processes_api/models/schema_one_of.py index 149bc99..004c5b9 100644 --- a/app/src/unity_sps_ogc_processes_api/models/schema_one_of.py +++ b/app/src/unity_sps_ogc_processes_api/models/schema_one_of.py @@ -20,7 +20,15 @@ import re # noqa: F401 from typing import Any, ClassVar, Dict, List, Optional, Union -from pydantic import BaseModel, Field, StrictBool, StrictFloat, StrictInt, StrictStr, field_validator +from pydantic import ( + BaseModel, + Field, + StrictBool, + StrictFloat, + StrictInt, + StrictStr, + field_validator, +) from typing_extensions import Annotated try: diff --git a/app/src/unity_sps_ogc_processes_api/models/static_indicator.py b/app/src/unity_sps_ogc_processes_api/models/static_indicator.py index de77c55..cd2fb7f 100644 --- a/app/src/unity_sps_ogc_processes_api/models/static_indicator.py +++ b/app/src/unity_sps_ogc_processes_api/models/static_indicator.py @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from pydantic import BaseModel, Field, StrictBool, StrictStr + from unity_sps_ogc_processes_api.models.job_control_options import JobControlOptions from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.metadata import Metadata diff --git a/app/src/unity_sps_ogc_processes_api/models/status_info.py b/app/src/unity_sps_ogc_processes_api/models/status_info.py index c662a02..c8969b0 100644 --- a/app/src/unity_sps_ogc_processes_api/models/status_info.py +++ b/app/src/unity_sps_ogc_processes_api/models/status_info.py @@ -23,6 +23,7 @@ from pydantic import BaseModel, Field, StrictStr, field_validator from typing_extensions import Annotated + from unity_sps_ogc_processes_api.models.exception import Exception from unity_sps_ogc_processes_api.models.link import Link from unity_sps_ogc_processes_api.models.status_code import StatusCode diff --git a/app/src/unity_sps_ogc_processes_api/security_api.py b/app/src/unity_sps_ogc_processes_api/security_api.py index a3c9b25..dfc6b32 100644 --- a/app/src/unity_sps_ogc_processes_api/security_api.py +++ b/app/src/unity_sps_ogc_processes_api/security_api.py @@ -13,4 +13,8 @@ OAuth2PasswordBearer, SecurityScopes, ) -from fastapi.security.api_key import APIKeyCookie, APIKeyHeader, APIKeyQuery # noqa: F401 +from fastapi.security.api_key import ( # noqa: F401 + APIKeyCookie, + APIKeyHeader, + APIKeyQuery, +) diff --git a/app/tests/conftest.py b/app/tests/conftest.py index 5e7c99d..04cda12 100644 --- a/app/tests/conftest.py +++ b/app/tests/conftest.py @@ -8,13 +8,18 @@ # from fastapi.encoders import jsonable_encoder from fastapi.testclient import TestClient -from openapi_server.database import Base -from openapi_server.database.models import Process -from openapi_server.utils.redis import RedisLock from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from sqlalchemy.pool import StaticPool -from unity_sps_ogc_processes_api.dependencies import get_db, get_redis_locking_client, get_settings + +from openapi_server.database import Base +from openapi_server.database.models import Process +from openapi_server.utils.redis import RedisLock +from unity_sps_ogc_processes_api.dependencies import ( + get_db, + get_redis_locking_client, + get_settings, +) from unity_sps_ogc_processes_api.main import app settings = get_settings() diff --git a/app/tests/test_api_api.py b/app/tests/test_api_api.py index 52a504f..8e29116 100644 --- a/app/tests/test_api_api.py +++ b/app/tests/test_api_api.py @@ -1,6 +1,7 @@ # coding: utf-8 from fastapi.testclient import TestClient + from unity_sps_ogc_processes_api.models.enumeration import Enumeration # noqa: F401 from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 diff --git a/app/tests/test_conformance_api.py b/app/tests/test_conformance_api.py index 9e18c2d..99d2372 100644 --- a/app/tests/test_conformance_api.py +++ b/app/tests/test_conformance_api.py @@ -1,6 +1,7 @@ # coding: utf-8 from fastapi.testclient import TestClient + from unity_sps_ogc_processes_api.models.conf_classes import ConfClasses # noqa: F401 from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 diff --git a/app/tests/test_dru_api.py b/app/tests/test_dru_api.py index ffd5ea8..d0188e1 100644 --- a/app/tests/test_dru_api.py +++ b/app/tests/test_dru_api.py @@ -5,6 +5,7 @@ import pytest from fastapi.testclient import TestClient + from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg diff --git a/app/tests/test_jobs_api.py b/app/tests/test_jobs_api.py index 0fa6d22..522bedf 100644 --- a/app/tests/test_jobs_api.py +++ b/app/tests/test_jobs_api.py @@ -1,8 +1,11 @@ # coding: utf-8 from fastapi.testclient import TestClient + from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 -from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData # noqa: F401 +from unity_sps_ogc_processes_api.models.inline_or_ref_data import ( # noqa: F401 + InlineOrRefData, +) from unity_sps_ogc_processes_api.models.job_list import JobList # noqa: F401 from unity_sps_ogc_processes_api.models.status_info import StatusInfo # noqa: F401 diff --git a/app/tests/test_landing_page_api.py b/app/tests/test_landing_page_api.py index cb2e455..d092aee 100644 --- a/app/tests/test_landing_page_api.py +++ b/app/tests/test_landing_page_api.py @@ -1,6 +1,7 @@ # coding: utf-8 from fastapi.testclient import TestClient + from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 from unity_sps_ogc_processes_api.models.landing_page import LandingPage # noqa: F401 diff --git a/app/tests/test_processes_api.py b/app/tests/test_processes_api.py index faac309..914bdd0 100644 --- a/app/tests/test_processes_api.py +++ b/app/tests/test_processes_api.py @@ -1,13 +1,22 @@ # coding: utf-8 from fastapi.testclient import TestClient + from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 -from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response # noqa: F401 -from unity_sps_ogc_processes_api.models.execute200_response1 import Execute200Response1 # noqa: F401 -from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows # noqa: F401 +from unity_sps_ogc_processes_api.models.execute200_response import ( # noqa: F401 + Execute200Response, +) +from unity_sps_ogc_processes_api.models.execute200_response1 import ( # noqa: F401 + Execute200Response1, +) +from unity_sps_ogc_processes_api.models.execute_workflows import ( # noqa: F401 + ExecuteWorkflows, +) from unity_sps_ogc_processes_api.models.process import Process # noqa: F401 from unity_sps_ogc_processes_api.models.process_list import ProcessList # noqa: F401 -from unity_sps_ogc_processes_api.models.processes_list import ProcessesList # noqa: F401 +from unity_sps_ogc_processes_api.models.processes_list import ( # noqa: F401 + ProcessesList, +) from unity_sps_ogc_processes_api.models.status_info import StatusInfo # noqa: F401 diff --git a/unity-test/conftest.py b/unity-test/conftest.py index 08a260c..65e8776 100644 --- a/unity-test/conftest.py +++ b/unity-test/conftest.py @@ -4,10 +4,6 @@ import fakeredis import pytest -from app.database import Base -from app.main import app, get_db, get_redis_locking_client, get_settings -from app.redis import RedisLock -from app.schemas.ogc_processes import Execute, Process, StatusCode, StatusInfo from fastapi import status from fastapi.encoders import jsonable_encoder from fastapi.testclient import TestClient @@ -15,6 +11,11 @@ from sqlalchemy.orm import sessionmaker from sqlalchemy.pool import StaticPool +from app.database import Base +from app.main import app, get_db, get_redis_locking_client, get_settings +from app.redis import RedisLock +from app.schemas.ogc_processes import Execute, Process, StatusCode, StatusInfo + settings = get_settings() SQLALCHEMY_DATABASE_URL = settings.DB_URL engine = create_engine( diff --git a/unity-test/test_main.py b/unity-test/test_main.py index 94fb1de..4c5c8d7 100644 --- a/unity-test/test_main.py +++ b/unity-test/test_main.py @@ -2,6 +2,9 @@ import os import pytest +from fastapi import status +from fastapi.encoders import jsonable_encoder + from app.schemas.ogc_processes import ( ConfClasses, Execute, @@ -14,8 +17,6 @@ StatusInfo, ) from app.schemas.unity_sps import HealthCheck -from fastapi import status -from fastapi.encoders import jsonable_encoder def test_get_landing_page(client): From d44b976bab734c95c97c3bab02348878801e4a86 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 17 Sep 2024 16:03:38 -0700 Subject: [PATCH 57/60] chore: update unit tests --- unity-test/__init__.py | 0 unity-test/conftest.py | 96 +++++++++++++------- unity-test/echoProcess.json | 73 ++++++++++++++++ unity-test/test_api_api.py | 39 +++++++++ unity-test/test_conformance_api.py | 22 +++++ unity-test/test_dru_api.py | 73 ++++++++++++++++ unity-test/test_jobs_api.py | 75 ++++++++++++++++ unity-test/test_landing_page_api.py | 22 +++++ unity-test/test_main.py | 131 ---------------------------- unity-test/test_processes_api.py | 64 ++++++++++++++ 10 files changed, 433 insertions(+), 162 deletions(-) delete mode 100644 unity-test/__init__.py create mode 100644 unity-test/echoProcess.json create mode 100644 unity-test/test_api_api.py create mode 100644 unity-test/test_conformance_api.py create mode 100644 unity-test/test_dru_api.py create mode 100644 unity-test/test_jobs_api.py create mode 100644 unity-test/test_landing_page_api.py delete mode 100644 unity-test/test_main.py create mode 100644 unity-test/test_processes_api.py diff --git a/unity-test/__init__.py b/unity-test/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/unity-test/conftest.py b/unity-test/conftest.py index 65e8776..c265749 100644 --- a/unity-test/conftest.py +++ b/unity-test/conftest.py @@ -5,16 +5,17 @@ import fakeredis import pytest from fastapi import status -from fastapi.encoders import jsonable_encoder + +# from fastapi.encoders import jsonable_encoder from fastapi.testclient import TestClient +from openapi_server.database import Base +from openapi_server.database.models import Process +from openapi_server.utils.redis import RedisLock from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from sqlalchemy.pool import StaticPool - -from app.database import Base -from app.main import app, get_db, get_redis_locking_client, get_settings -from app.redis import RedisLock -from app.schemas.ogc_processes import Execute, Process, StatusCode, StatusInfo +from unity_sps_ogc_processes_api.dependencies import get_db, get_redis_locking_client, get_settings +from unity_sps_ogc_processes_api.main import app settings = get_settings() SQLALCHEMY_DATABASE_URL = settings.DB_URL @@ -59,7 +60,8 @@ def fake_filesystem(fs_session, test_directory): # pylint:disable=invalid-name fs_session.add_real_directory(os.path.join(test_directory), "test_data") fs_session.create_dir(settings.DAG_CATALOG_DIRECTORY) fs_session.create_file( - os.path.join(settings.DAG_CATALOG_DIRECTORY, "cwltool_help_dag.py"), contents="test" + os.path.join(settings.DAG_CATALOG_DIRECTORY, "cwltool_help_dag.py"), + contents="test", ) fs_session.create_file(os.path.join(settings.DAG_CATALOG_DIRECTORY, "EchoProcess.py"), contents="test") fs_session.create_dir(settings.DEPLOYED_DAGS_DIRECTORY) @@ -94,7 +96,12 @@ def mock_get_existing_dag(requests_mock): "file_token": "string", "owners": ["string"], "description": "string", - "schedule_interval": {"__type": "string", "days": 0, "seconds": 0, "microseconds": 0}, + "schedule_interval": { + "__type": "string", + "days": 0, + "seconds": 0, + "microseconds": 0, + }, "timetable_description": "string", "tags": [{"name": "string"}], "max_active_tasks": 0, @@ -125,7 +132,12 @@ def mock_get_existing_dag(requests_mock): "file_token": "string", "owners": ["string"], "description": "string", - "schedule_interval": {"__type": "string", "days": 0, "seconds": 0, "microseconds": 0}, + "schedule_interval": { + "__type": "string", + "days": 0, + "seconds": 0, + "microseconds": 0, + }, "timetable_description": "string", "tags": [{"name": "string"}], "max_active_tasks": 0, @@ -165,7 +177,12 @@ def mock_patch_existing_dag(requests_mock): "file_token": "string", "owners": ["string"], "description": "string", - "schedule_interval": {"__type": "string", "days": 0, "seconds": 0, "microseconds": 0}, + "schedule_interval": { + "__type": "string", + "days": 0, + "seconds": 0, + "microseconds": 0, + }, "timetable_description": "string", "tags": [{"name": "string"}], "max_active_tasks": 0, @@ -196,7 +213,12 @@ def mock_patch_existing_dag(requests_mock): "file_token": "string", "owners": ["string"], "description": "string", - "schedule_interval": {"__type": "string", "days": 0, "seconds": 0, "microseconds": 0}, + "schedule_interval": { + "__type": "string", + "days": 0, + "seconds": 0, + "microseconds": 0, + }, "timetable_description": "string", "tags": [{"name": "string"}], "max_active_tasks": 0, @@ -216,7 +238,8 @@ def mock_patch_existing_dag(requests_mock): @pytest.fixture(scope="function", autouse=True) def mock_delete_existing_dag(requests_mock): return requests_mock.delete( - re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)$"), status_code=status.HTTP_204_NO_CONTENT + re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)$"), + status_code=status.HTTP_204_NO_CONTENT, ) @@ -417,7 +440,12 @@ def mock_get_existing_running_dag_dagrun_tasks(requests_mock): def mock_patch_existing_running_dag_dagrun_task(requests_mock): return requests_mock.patch( re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)/taskInstances/([^/]*)$"), - json={"task_id": "string", "dag_id": "string", "execution_date": "string", "dag_run_id": "string"}, + json={ + "task_id": "string", + "dag_id": "string", + "execution_date": "string", + "dag_run_id": "string", + }, ) @@ -439,21 +467,27 @@ def deploy_process(test_directory, client): assert response.status_code == status.HTTP_204_NO_CONTENT -@pytest.fixture(scope="function") -def execute_process(test_directory, mock_post_existing_dag_new_dagrun, client, deploy_process): - data_filename = os.path.join(test_directory, "test_data/execution_requests/execute_cwltool_help_dag.json") - f = open(data_filename) - execute_json = json.load(f) - execute = Execute.model_validate(execute_json) - response = client.post(f"/processes/{deploy_process.id}/execution", json=jsonable_encoder(execute)) - assert response.status_code == status.HTTP_200_OK - data = response.json() - status_info = StatusInfo.model_validate(data) - - yield status_info - - response = client.delete(f"/jobs/{status_info.jobID}") - assert response.status_code == status.HTTP_200_OK - data = response.json() - status_info = StatusInfo.model_validate(data) - assert status_info.status == StatusCode.dismissed.value +# @pytest.fixture(scope="function") +# def execute_process( +# test_directory, mock_post_existing_dag_new_dagrun, client, deploy_process +# ): +# data_filename = os.path.join( +# test_directory, "test_data/execution_requests/execute_cwltool_help_dag.json" +# ) +# f = open(data_filename) +# execute_json = json.load(f) +# execute = Execute.model_validate(execute_json) +# response = client.post( +# f"/processes/{deploy_process.id}/execution", json=jsonable_encoder(execute) +# ) +# assert response.status_code == status.HTTP_200_OK +# data = response.json() +# status_info = StatusInfo.model_validate(data) + +# yield status_info + +# response = client.delete(f"/jobs/{status_info.jobID}") +# assert response.status_code == status.HTTP_200_OK +# data = response.json() +# status_info = StatusInfo.model_validate(data) +# assert status_info.status == StatusCode.dismissed.value diff --git a/unity-test/echoProcess.json b/unity-test/echoProcess.json new file mode 100644 index 0000000..9d2405a --- /dev/null +++ b/unity-test/echoProcess.json @@ -0,0 +1,73 @@ +{ + "executionUnit": { + "additional_properties": {}, + "config": { + "additional_properties": {} + }, + "deployment": "cloud", + "image": "example/image:latest", + "type": "docker" + }, + "processDescription": { + "description": "This process performs an example task.", + "id": "EchoProcess", + "inputs": { + "input1": { + "description": "Description of Input 1", + "maxOccurs": 1, + "minOccurs": 1, + "schema": { + "$ref": "#/components/schemas/ExampleSchema" + }, + "title": "Input 1 Title" + } + }, + "jobControlOptions": [ + "sync-execute", + "async-execute" + ], + "keywords": [ + "example", + "process", + "OGC" + ], + "links": [ + { + "href": "http://example.com/process", + "hreflang": "en", + "rel": "self", + "title": "Process Description", + "type": "application/json" + } + ], + "metadata": [ + { + "href": "http://example.com/metadata", + "hreflang": "en", + "rel": "related", + "title": "Example Metadata", + "type": "application/json" + } + ], + "outputs": { + "output1": { + "description": "Description of Output 1", + "schema": { + "deprecated": false, + "exclusiveMaximum": false, + "exclusiveMinimum": false, + "minItems": 0, + "minLength": 0, + "minProperties": 0, + "nullable": false, + "readOnly": false, + "uniqueItems": false, + "writeOnly": false + }, + "title": "Output 1 Title" + } + }, + "title": "Example Process Title", + "version": "1.0.0" + } +} diff --git a/unity-test/test_api_api.py b/unity-test/test_api_api.py new file mode 100644 index 0000000..52a504f --- /dev/null +++ b/unity-test/test_api_api.py @@ -0,0 +1,39 @@ +# coding: utf-8 + +from fastapi.testclient import TestClient +from unity_sps_ogc_processes_api.models.enumeration import Enumeration # noqa: F401 +from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 + + +def test_get_api(client: TestClient): + """Test case for get_api + + Retrieve this API definition. + """ + # uncomment below to make a request + # response = client.request( + # "GET", + # "/api", + # headers=headers, + # params=params, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 + + +def test_get_api_processes(client: TestClient): + """Test case for get_api_processes + + Retrieve the list of processes available from this API implementation & deployment. + """ + # uncomment below to make a request + # response = client.request( + # "GET", + # "/api/processes-list", + # headers=headers, + # params=params, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 diff --git a/unity-test/test_conformance_api.py b/unity-test/test_conformance_api.py new file mode 100644 index 0000000..9e18c2d --- /dev/null +++ b/unity-test/test_conformance_api.py @@ -0,0 +1,22 @@ +# coding: utf-8 + +from fastapi.testclient import TestClient +from unity_sps_ogc_processes_api.models.conf_classes import ConfClasses # noqa: F401 +from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 + + +def test_get_conformance(client: TestClient): + """Test case for get_conformance + + Retrieve the set of OGC API conformance classes that are supported by this service. + """ + # uncomment below to make a request + # response = client.request( + # "GET", + # "/conformance", + # headers=headers, + # params=params, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 diff --git a/unity-test/test_dru_api.py b/unity-test/test_dru_api.py new file mode 100644 index 0000000..ffd5ea8 --- /dev/null +++ b/unity-test/test_dru_api.py @@ -0,0 +1,73 @@ +# coding: utf-8 + +import json +import os + +import pytest +from fastapi.testclient import TestClient +from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg + + +@pytest.fixture +def sample_ogcapppkg(): + # Get the current directory of the test file + current_dir = os.path.dirname(os.path.abspath(__file__)) + + # Construct the path to echoProcess.json + json_file_path = os.path.join(current_dir, "echoProcess.json") + + # Read the JSON file + with open(json_file_path, "r") as file: + data = json.load(file) + + return Ogcapppkg(**data) + + +def test_deploy(client: TestClient, sample_ogcapppkg): + """Test case for deploy""" + response = client.post( + "/processes", + json=sample_ogcapppkg.model_dump(exclude_none=True, by_alias=True), + ) + assert response.status_code == 201 + + +def test_replace(client: TestClient, sample_ogcapppkg): + """Test case for replace""" + process_id = "EchoProcess" + response = client.put( + f"/processes/{process_id}", + json=sample_ogcapppkg.model_dump(), + ) + assert response.status_code == 204 + + +def test_undeploy(client: TestClient): + """Test case for undeploy""" + process_id = "EchoProcess" + response = client.delete(f"/processes/{process_id}", params={"force": True}) + assert response.status_code == 204 + + +def test_deploy_conflict(client: TestClient, sample_ogcapppkg): + """Test case for deploy when process already exists""" + # First, deploy the process + response = client.post( + "/processes", + json=sample_ogcapppkg.model_dump(exclude_none=True, by_alias=True), + ) + # Try to deploy the same process again + response = client.post( + "/processes", + json=sample_ogcapppkg.model_dump(exclude_none=True, by_alias=True), + ) + assert response.status_code == 409 + assert "already exists" in response.json()["detail"] + + +def test_undeploy_not_found(client: TestClient): + """Test case for undeploy when process doesn't exist""" + process_id = "non_existent_process" + response = client.delete(f"/processes/{process_id}") + assert response.status_code == 404 + assert "not found" in response.json()["detail"] diff --git a/unity-test/test_jobs_api.py b/unity-test/test_jobs_api.py new file mode 100644 index 0000000..0fa6d22 --- /dev/null +++ b/unity-test/test_jobs_api.py @@ -0,0 +1,75 @@ +# coding: utf-8 + +from fastapi.testclient import TestClient +from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 +from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData # noqa: F401 +from unity_sps_ogc_processes_api.models.job_list import JobList # noqa: F401 +from unity_sps_ogc_processes_api.models.status_info import StatusInfo # noqa: F401 + + +def test_dismiss(client: TestClient): + """Test case for dismiss + + cancel a job execution, remove a finished job + """ + + # uncomment below to make a request + # response = client.request( + # "DELETE", + # "/jobs/{jobId}".format(jobId='job_id_example'), + # headers=headers, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 + + +def test_get_jobs(client: TestClient): + """Test case for get_jobs + + retrieve the list of jobs. + """ + + # uncomment below to make a request + # response = client.request( + # "GET", + # "/jobs", + # headers=headers, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 + + +def test_get_result(client: TestClient): + """Test case for get_result + + retrieve the result(s) of a job + """ + + # uncomment below to make a request + # response = client.request( + # "GET", + # "/jobs/{jobId}/results".format(jobId='job_id_example'), + # headers=headers, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 + + +def test_get_status(client: TestClient): + """Test case for get_status + + retrieve the status of a job + """ + + # uncomment below to make a request + # response = client.request( + # "GET", + # "/jobs/{jobId}".format(jobId='job_id_example'), + # headers=headers, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 diff --git a/unity-test/test_landing_page_api.py b/unity-test/test_landing_page_api.py new file mode 100644 index 0000000..cb2e455 --- /dev/null +++ b/unity-test/test_landing_page_api.py @@ -0,0 +1,22 @@ +# coding: utf-8 + +from fastapi.testclient import TestClient +from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 +from unity_sps_ogc_processes_api.models.landing_page import LandingPage # noqa: F401 + + +def test_get_landing_page(client: TestClient): + """Test case for get_landing_page + + Retrieve the OGC API landing page for this service. + """ + # uncomment below to make a request + # response = client.request( + # "GET", + # "/", + # headers=headers, + # params=params, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 diff --git a/unity-test/test_main.py b/unity-test/test_main.py deleted file mode 100644 index 4c5c8d7..0000000 --- a/unity-test/test_main.py +++ /dev/null @@ -1,131 +0,0 @@ -import json -import os - -import pytest -from fastapi import status -from fastapi.encoders import jsonable_encoder - -from app.schemas.ogc_processes import ( - ConfClasses, - Execute, - JobList, - LandingPage, - Process, - ProcessList, - Results, - StatusCode, - StatusInfo, -) -from app.schemas.unity_sps import HealthCheck - - -def test_get_landing_page(client): - response = client.get("/") - assert response.status_code == status.HTTP_200_OK - data = response.json() - landing_page = LandingPage.model_validate(data) - assert landing_page.title == "Unity SPS Processing Server" - - -def test_get_health(client): - response = client.get("/health") - assert response.status_code == status.HTTP_200_OK - data = response.json() - landing_page = HealthCheck.model_validate(data) - assert landing_page.status == "OK" - - -def test_get_conformance_declaration(client): - response = client.get("/conformance") - assert response.status_code == status.HTTP_200_OK - data = response.json() - conformance_declaration = ConfClasses.model_validate(data) - assert len(conformance_declaration.conformsTo) > 0 - assert conformance_declaration.conformsTo == [ - "http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/ogc-process-description" - ] - - -@pytest.mark.dependency() -def test_post_deploy_process(test_directory, client): - data_filename = os.path.join(test_directory, "test_data/process_descriptions/EchoProcess.json") - f = open(data_filename) - process_json = json.load(f) - process = Process.model_validate(process_json) - response = client.post("/processes", json=process.model_dump()) - assert response.status_code == status.HTTP_200_OK - data = response.json() - process = Process.model_validate(data) - assert process.id == "EchoProcess" - - -@pytest.mark.dependency(depends=["test_post_deploy_process"]) -def test_delete_undeploy_process(client): - response = client.delete("/processes/EchoProcess") - assert response.status_code == status.HTTP_409_CONFLICT - response = client.delete("/processes/EchoProcess", params={"force": True}) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -def test_post_execute_process(test_directory, client, deploy_process): - data_filename = os.path.join(test_directory, "test_data/execution_requests/execute_cwltool_help_dag.json") - f = open(data_filename) - execute_json = json.load(f) - execute = Execute.model_validate(execute_json) - response = client.post(f"/processes/{deploy_process.id}/execution", json=jsonable_encoder(execute)) - assert response.status_code == status.HTTP_200_OK - data = response.json() - assert StatusInfo.model_validate(data) - - -def test_delete_dismiss_execution(test_directory, client, deploy_process): - data_filename = os.path.join(test_directory, "test_data/execution_requests/execute_cwltool_help_dag.json") - f = open(data_filename) - execute_json = json.load(f) - execute = Execute.model_validate(execute_json) - response = client.post(f"/processes/{deploy_process.id}/execution", json=jsonable_encoder(execute)) - data = response.json() - status_info = StatusInfo.model_validate(data) - - response = client.delete(f"/jobs/{status_info.jobID}") - assert response.status_code == status.HTTP_200_OK - data = response.json() - status_info = StatusInfo.model_validate(data) - assert status_info.status == StatusCode.dismissed.value - - -def test_get_process_description(client, deploy_process): - response = client.get(f"/processes/{deploy_process.id}") - assert response.status_code == status.HTTP_200_OK - data = response.json() - process = Process.model_validate(data) - assert process.id == deploy_process.id - - -def test_get_process_list(client): - response = client.get("/processes") - assert response.status_code == status.HTTP_200_OK - data = response.json() - assert ProcessList.model_validate(data) - - -def test_get_job_list(client): - response = client.get("/jobs") - assert response.status_code == status.HTTP_200_OK - data = response.json() - assert JobList.model_validate(data) - - -def test_get_status(client, execute_process): - response = client.get(f"/jobs/{execute_process.jobID}") - assert response.status_code == status.HTTP_200_OK - data = response.json() - status_info = StatusInfo.model_validate(data) - assert status_info.jobID == execute_process.jobID - - -def test_get_results(client, execute_process): - response = client.get(f"/jobs/{execute_process.jobID}/results") - assert response.status_code == status.HTTP_200_OK - data = response.json() - assert Results.model_validate(data) diff --git a/unity-test/test_processes_api.py b/unity-test/test_processes_api.py new file mode 100644 index 0000000..faac309 --- /dev/null +++ b/unity-test/test_processes_api.py @@ -0,0 +1,64 @@ +# coding: utf-8 + +from fastapi.testclient import TestClient +from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 +from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response # noqa: F401 +from unity_sps_ogc_processes_api.models.execute200_response1 import Execute200Response1 # noqa: F401 +from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows # noqa: F401 +from unity_sps_ogc_processes_api.models.process import Process # noqa: F401 +from unity_sps_ogc_processes_api.models.process_list import ProcessList # noqa: F401 +from unity_sps_ogc_processes_api.models.processes_list import ProcessesList # noqa: F401 +from unity_sps_ogc_processes_api.models.status_info import StatusInfo # noqa: F401 + + +def test_execute(client: TestClient): + """Test case for execute + + execute a process. + """ + ExecuteWorkflows() + # uncomment below to make a request + # response = client.request( + # "POST", + # "/processes/{processId}/execution".format(processId=unity_sps_ogc_processes_api.ProcessesList()), + # headers=headers, + # json=execute_workflows, + # params=params, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 + + +def test_get_process_description(client: TestClient): + """Test case for get_process_description + + retrieve a process description + """ + + # uncomment below to make a request + # response = client.request( + # "GET", + # "/processes/{processId}".format(processId=unity_sps_ogc_processes_api.ProcessesList()), + # headers=headers, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 + + +def test_get_processes(client: TestClient): + """Test case for get_processes + + retrieve the list of available processes + """ + + # uncomment below to make a request + # response = client.request( + # "GET", + # "/processes", + # headers=headers, + # ) + + # uncomment below to assert the status code of the HTTP response + # assert response.status_code == 200 From b98fc9f7649ccb0ec58cd9e4cc1b3a4c0f8bc674 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 17 Sep 2024 16:18:01 -0700 Subject: [PATCH 58/60] chore: update unit tests --- unity-test/conftest.py | 17 +- .../execution_requests/EchoProcess.json | 120 ------ .../test_data/execution_requests/cwl_dag.json | 12 - .../execution_requests/cwltool_help_dag.json | 120 ------ .../process_descriptions/EchoProcess.json | 360 ------------------ .../process_descriptions/cwl_dag.json | 48 --- .../cwltool_help_dag.json | 360 ------------------ unity-test/test_main.py | 164 -------- 8 files changed, 5 insertions(+), 1196 deletions(-) delete mode 100644 unity-test/test_data/execution_requests/EchoProcess.json delete mode 100644 unity-test/test_data/execution_requests/cwl_dag.json delete mode 100644 unity-test/test_data/execution_requests/cwltool_help_dag.json delete mode 100644 unity-test/test_data/process_descriptions/EchoProcess.json delete mode 100644 unity-test/test_data/process_descriptions/cwl_dag.json delete mode 100644 unity-test/test_data/process_descriptions/cwltool_help_dag.json delete mode 100644 unity-test/test_main.py diff --git a/unity-test/conftest.py b/unity-test/conftest.py index 1738d68..c265749 100644 --- a/unity-test/conftest.py +++ b/unity-test/conftest.py @@ -1,7 +1,5 @@ -import glob import json import os -import pathlib import re import fakeredis @@ -30,11 +28,6 @@ Base.metadata.create_all(bind=engine) -TEST_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_data") -PROCESS_FILES = glob.glob(f"{TEST_DIR}/process_descriptions/*.json") -EXECUTION_FILES = glob.glob(f"{TEST_DIR}/execution_requests/*.json") - - def override_get_db(): try: db = TestingSessionLocal() @@ -71,7 +64,6 @@ def fake_filesystem(fs_session, test_directory): # pylint:disable=invalid-name contents="test", ) fs_session.create_file(os.path.join(settings.DAG_CATALOG_DIRECTORY, "EchoProcess.py"), contents="test") - fs_session.create_file(os.path.join(settings.DAG_CATALOG_DIRECTORY, "cwl_dag.py"), contents="test") fs_session.create_dir(settings.DEPLOYED_DAGS_DIRECTORY) yield fs_session @@ -457,16 +449,17 @@ def mock_patch_existing_running_dag_dagrun_task(requests_mock): ) -@pytest.fixture(scope="function", params=PROCESS_FILES) -def deploy_process(test_directory, client, request): - f = open(request.param) +@pytest.fixture(scope="function") +def deploy_process(test_directory, client): + data_filename = os.path.join(test_directory, "..", "process_descriptions", "cwltool_help_dag.json") + f = open(data_filename) process_json = json.load(f) process = Process.model_validate(process_json) response = client.post("/processes", json=process.model_dump()) assert response.status_code == status.HTTP_200_OK data = response.json() process = Process.model_validate(data) - assert process.id == pathlib.Path(request.param).stem + assert process.id == "cwltool_help_dag" yield process diff --git a/unity-test/test_data/execution_requests/EchoProcess.json b/unity-test/test_data/execution_requests/EchoProcess.json deleted file mode 100644 index a57656b..0000000 --- a/unity-test/test_data/execution_requests/EchoProcess.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "inputs": { - "arrayInput": [ - 1, - 2, - 3, - 4, - 5, - 6 - ], - "boundingBoxInput": { - "bbox": [ - 51.9, - 7, - 52, - 7.1 - ], - "crs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84" - }, - "complexObjectInput": { - "value": { - "property1": "value1", - "property2": "value2", - "property5": true - } - }, - "dateInput": "2021-03-06T07:21:00", - "doubleInput": 3.14159, - "featureCollectionInput": { - "mediaType": "application/gml+xml; version=3.2", - "value": "..." - }, - "geometryInput": [ - { - "mediaType": "application/gml+xml; version=3.2", - "value": "-77.024519 38.810529 -77.024635 38.810973 -77.024704 38.810962 -77.024776 38.811239 -77.024957 38.81121 -77.024905 38.811012 -77.024905 38.811012 -77.024865 38.810857 -77.025024 38.810832 -77.025071 38.811012 -77.025203 38.810992 -77.02506 38.810444 -77.024519 38.810529" - }, - { - "value": { - "coordinates": [ - [ - [ - -176.5814819, - -44.10896301 - ], - [ - -176.5818024, - -44.10964584 - ], - [ - -176.5844116, - -44.11236572 - ], - [ - -176.5935974, - -44.11021805 - ], - [ - -176.5973511, - -44.10743332 - ], - [ - -176.5950928, - -44.10562134 - ], - [ - -176.5858459, - -44.1043396 - ], - [ - -176.5811157, - -44.10667801 - ], - [ - -176.5814819, - -44.10896301 - ] - ] - ], - "type": "Polygon" - } - } - ], - "imagesInput": [ - { - "href": "https://www.someserver.com/ogcapi/Daraa/collections/Daraa_DTED/styles/Topographic/coverage?...", - "type": "image/tiff; application=geotiff" - }, - { - "encoding": "base64", - "mediaType": "image/jp2", - "value": "VBORw0KGgoAAAANSUhEUgAABvwAAAa4CAYAAABMB35kAAABhGlDQ1BJQ0MgcHJvZmlsZQAA\nKJF9kT1Iw0AcxV9TpSL1A+xQxCFDdbIgKuKoVShChVArtOpgcumH0KQhSXFxFFwLDn4sVh1c\nnHV1cBUEwQ8QNzcnRRcp8X9JoUWMB8f9eHfvcfcOEOplplkdY4Cm22Y6mRCzuRUx9IogouhH\n ... \nj3Z5mX7/PCPVRJV92rpHK24xcJrzk20+tkeYlCPqcZNO3Lpni1OJWatPCcmgGDEqx7Om6lfa\nppM4k4BTe9+bsn3L9/9/yWhA0PwQGW8ipCZsnZt9lsdrYEM8z/M8z/M8z/M8z/M8z/MzLWY1\nAAAACUlEQVQ871H6P6JI+TxS5Wn2AAAAAElFTkSuQmCC" - } - ], - "measureInput": { - "value": { - "measurement": 10.3, - "reference": "https://ucum.org/ucum-essence.xml", - "uom": "m" - } - }, - "stringInput": "Value2" - }, - "outputs": { - "arrayOutput": {}, - "boundingBoxOutput": {}, - "complexObjectOutput": {}, - "dateOutput": {}, - "doubleOutput": {}, - "featureCollectionOutput": {}, - "geometryOutput": {}, - "imageOutput": { - "format": { - "mediaType": "image/tiff; application=geotiff" - } - }, - "measureOutput": {}, - "stringOutput": {} - } -} diff --git a/unity-test/test_data/execution_requests/cwl_dag.json b/unity-test/test_data/execution_requests/cwl_dag.json deleted file mode 100644 index 42db1ba..0000000 --- a/unity-test/test_data/execution_requests/cwl_dag.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "inputs": { - "complexObjectInput": { - "value": { - "message": "Hello Unity" - } - } - }, - "outputs": { - "stringOutput": {} - } -} diff --git a/unity-test/test_data/execution_requests/cwltool_help_dag.json b/unity-test/test_data/execution_requests/cwltool_help_dag.json deleted file mode 100644 index a57656b..0000000 --- a/unity-test/test_data/execution_requests/cwltool_help_dag.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "inputs": { - "arrayInput": [ - 1, - 2, - 3, - 4, - 5, - 6 - ], - "boundingBoxInput": { - "bbox": [ - 51.9, - 7, - 52, - 7.1 - ], - "crs": "http://www.opengis.net/def/crs/OGC/1.3/CRS84" - }, - "complexObjectInput": { - "value": { - "property1": "value1", - "property2": "value2", - "property5": true - } - }, - "dateInput": "2021-03-06T07:21:00", - "doubleInput": 3.14159, - "featureCollectionInput": { - "mediaType": "application/gml+xml; version=3.2", - "value": "..." - }, - "geometryInput": [ - { - "mediaType": "application/gml+xml; version=3.2", - "value": "-77.024519 38.810529 -77.024635 38.810973 -77.024704 38.810962 -77.024776 38.811239 -77.024957 38.81121 -77.024905 38.811012 -77.024905 38.811012 -77.024865 38.810857 -77.025024 38.810832 -77.025071 38.811012 -77.025203 38.810992 -77.02506 38.810444 -77.024519 38.810529" - }, - { - "value": { - "coordinates": [ - [ - [ - -176.5814819, - -44.10896301 - ], - [ - -176.5818024, - -44.10964584 - ], - [ - -176.5844116, - -44.11236572 - ], - [ - -176.5935974, - -44.11021805 - ], - [ - -176.5973511, - -44.10743332 - ], - [ - -176.5950928, - -44.10562134 - ], - [ - -176.5858459, - -44.1043396 - ], - [ - -176.5811157, - -44.10667801 - ], - [ - -176.5814819, - -44.10896301 - ] - ] - ], - "type": "Polygon" - } - } - ], - "imagesInput": [ - { - "href": "https://www.someserver.com/ogcapi/Daraa/collections/Daraa_DTED/styles/Topographic/coverage?...", - "type": "image/tiff; application=geotiff" - }, - { - "encoding": "base64", - "mediaType": "image/jp2", - "value": "VBORw0KGgoAAAANSUhEUgAABvwAAAa4CAYAAABMB35kAAABhGlDQ1BJQ0MgcHJvZmlsZQAA\nKJF9kT1Iw0AcxV9TpSL1A+xQxCFDdbIgKuKoVShChVArtOpgcumH0KQhSXFxFFwLDn4sVh1c\nnHV1cBUEwQ8QNzcnRRcp8X9JoUWMB8f9eHfvcfcOEOplplkdY4Cm22Y6mRCzuRUx9IogouhH\n ... \nj3Z5mX7/PCPVRJV92rpHK24xcJrzk20+tkeYlCPqcZNO3Lpni1OJWatPCcmgGDEqx7Om6lfa\nppM4k4BTe9+bsn3L9/9/yWhA0PwQGW8ipCZsnZt9lsdrYEM8z/M8z/M8z/M8z/M8z/MzLWY1\nAAAACUlEQVQ871H6P6JI+TxS5Wn2AAAAAElFTkSuQmCC" - } - ], - "measureInput": { - "value": { - "measurement": 10.3, - "reference": "https://ucum.org/ucum-essence.xml", - "uom": "m" - } - }, - "stringInput": "Value2" - }, - "outputs": { - "arrayOutput": {}, - "boundingBoxOutput": {}, - "complexObjectOutput": {}, - "dateOutput": {}, - "doubleOutput": {}, - "featureCollectionOutput": {}, - "geometryOutput": {}, - "imageOutput": { - "format": { - "mediaType": "image/tiff; application=geotiff" - } - }, - "measureOutput": {}, - "stringOutput": {} - } -} diff --git a/unity-test/test_data/process_descriptions/EchoProcess.json b/unity-test/test_data/process_descriptions/EchoProcess.json deleted file mode 100644 index 46f218e..0000000 --- a/unity-test/test_data/process_descriptions/EchoProcess.json +++ /dev/null @@ -1,360 +0,0 @@ -{ - "description": "This process accepts and number of input and simple echoes each input as an output.", - "id": "EchoProcess", - "inputs": [ - { - "arrayInput": { - "description": "This is an example of a single process input that is an array of values. In this case, the input array would be interpreted as a single value and not as individual inputs.", - "schema": { - "items": { - "type": "integer" - }, - "maxItems": 10, - "minItems": 2, - "type": "array" - }, - "title": "Array Input Example" - }, - "boundingBoxInput": { - "description": "This is an example of a BBOX literal input.", - "schema": { - "allOf": [ - { - "format": "ogc-bbox" - }, - { - "$ref": "../../openapi/schemas/bbox.yaml" - } - ] - }, - "title": "Bounding Box Input Example" - }, - "complexObjectInput": { - "description": "This is an example of a complex object input.", - "schema": { - "properties": { - "property1": { - "type": "string" - }, - "property2": { - "format": "uri", - "type": "string" - }, - "property3": { - "type": "number" - }, - "property4": { - "format": "date-time", - "type": "string" - }, - "property5": { - "type": "boolean" - } - }, - "required": [ - "property1", - "property5" - ], - "type": "object" - }, - "title": "Complex Object Input Example" - }, - "dateInput": { - "description": "This is an example of a DATE literal input.", - "schema": { - "format": "date-time", - "type": "string" - }, - "title": "Date Literal Input Example" - }, - "doubleInput": { - "description": "This is an example of a DOUBLE literal input that is bounded between a value greater than 0 and 10. The default value is 5.", - "schema": { - "default": 5, - "exclusiveMinimum": true, - "format": "double", - "maximum": 10, - "minimum": 0, - "type": "number" - }, - "title": "Bounded Double Literal Input Example" - }, - "featureCollectionInput": { - "description": "This is an example of an input that is a feature collection that can be encoded in one of three ways: as a GeoJSON feature collection, as a GML feature collection retrieved from a WFS or as a KML document.", - "schema": { - "oneOf": [ - { - "contentMediaType": "application/gml+xml; version=3.2", - "type": "string" - }, - { - "contentMediaType": "application/vnd.google-earth.kml+xml", - "contentSchema": "https://schemas.opengis.net/kml/2.3/ogckml23.xsd", - "type": "string" - }, - { - "allOf": [ - { - "format": "geojson-feature-collection" - }, - { - "$ref": "https://geojson.org/schema/FeatureCollection.json" - } - ] - } - ] - }, - "title": "Feature Collection Input Example." - }, - "geometryInput": { - "description": "This is an example of a geometry input. In this case the geometry can be expressed as a GML of GeoJSON geometry.", - "maxOccurs": 5, - "minOccurs": 2, - "schema": { - "oneOf": [ - { - "contentMediaType": "application/gml+xml; version=3.2", - "contentSchema": "http://schemas.opengis.net/gml/3.2.1/geometryBasic2d.xsd", - "type": "string" - }, - { - "format": "geojson-geometry" - } - ] - }, - "title": "Geometry input" - }, - "imagesInput": { - "description": "This is an example of an image input. In this case, the input is an array of up to 150 images that might, for example, be a set of tiles. The oneOf[] conditional is used to indicate the acceptable image content types; GeoTIFF and JPEG 2000 in this case. Each input image in the input array can be included inline in the execute request as a base64-encoded string or referenced using the link.yaml schema. The use of a base64-encoded string is implied by the specification and does not need to be specified in the definition of the input.", - "maxOccurs": 150, - "minOccurs": 1, - "schema": { - "oneOf": [ - { - "contentEncoding": "binary", - "contentMediaType": "image/tiff; application=geotiff", - "type": "string" - }, - { - "contentEncoding": "binary", - "contentMediaType": "image/jp2", - "type": "string" - } - ] - }, - "title": "Inline Images Value Input" - }, - "measureInput": { - "description": "This is an example of a NUMERIC literal with an associated unit of measure.", - "schema": { - "properties": { - "measurement": { - "type": "number" - }, - "reference": { - "format": "uri", - "type": "string" - }, - "uom": { - "type": "string" - } - }, - "required": [ - "measurement", - "uom" - ], - "type": "object" - }, - "title": "Numerical Value with UOM Example" - }, - "stringInput": { - "description": "This is an example of a STRING literal input.", - "schema": { - "enum": [ - "Value1", - "Value2", - "Value3" - ], - "type": "string" - }, - "title": "String Literal Input Example" - } - } - ], - "jobControlOptions": [ - "async-execute", - "sync-execute" - ], - "links": [ - { - "href": "https://processing.example.org/oapi-p/processes/EchoProcess/execution", - "rel": "http://www.opengis.net/def/rel/ogc/1.0/execute", - "title": "Execute endpoint" - } - ], - "outputs": [ - { - "arrayOutput": { - "schema": { - "items": { - "type": "integer" - }, - "maxItems": 10, - "minItems": 2, - "type": "array" - } - }, - "boundingBoxOutput": { - "schema": { - "allOf": [ - { - "format": "ogc-bbox" - }, - { - "$ref": "../../openapi/schemas/bbox.yaml" - } - ] - } - }, - "complexObjectOutput": { - "schema": { - "properties": { - "property1": { - "type": "string" - }, - "property2": { - "format": "uri", - "type": "string" - }, - "property3": { - "type": "number" - }, - "property4": { - "format": "date-time", - "type": "string" - }, - "property5": { - "type": "boolean" - } - }, - "required": [ - "property1", - "property5" - ], - "type": "object" - } - }, - "dateOutput": { - "schema": { - "format": "date-time", - "type": "string" - } - }, - "doubleOutput": { - "schema": { - "default": 5, - "exclusiveMinimum": true, - "format": "double", - "maximum": 10, - "minimum": 0, - "type": "number" - } - }, - "featureCollectionOutput": { - "schema": { - "oneOf": [ - { - "contentMediaType": "application/gml+xml; version=3.2", - "type": "string" - }, - { - "contentMediaType": "application/vnd.google-earth.kml+xml", - "contentSchema": "https://schemas.opengis.net/kml/2.3/ogckml23.xsd", - "type": "string" - }, - { - "allOf": [ - { - "format": "geojson-feature-collection" - }, - { - "$ref": "https://geojson.org/schema/FeatureCollection.json" - } - ] - } - ] - } - }, - "geometryOutput": { - "schema": { - "oneOf": [ - { - "contentMediaType": "application/gml+xml", - "contentSchema": "http://schemas.opengis.net/gml/3.2.1/geometryBasic2d.xsd", - "type": "string" - }, - { - "allOf": [ - { - "format": "geojson-geometry" - }, - { - "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/geometryGeoJSON.yaml" - } - ] - } - ] - } - }, - "imagesOutput": { - "schema": { - "oneOf": [ - { - "contentEncoding": "binary", - "contentMediaType": "image/tiff; application=geotiff", - "type": "string" - }, - { - "contentEncoding": "binary", - "contentMediaType": "image/jp2", - "type": "string" - } - ] - } - }, - "measureOutput": { - "schema": { - "properties": { - "measurement": { - "type": "number" - }, - "reference": { - "format": "uri", - "type": "string" - }, - "uom": { - "type": "string" - } - }, - "required": [ - "measurement", - "uom" - ], - "type": "object" - } - }, - "stringOutput": { - "schema": { - "enum": [ - "Value1", - "Value2", - "Value3" - ], - "type": "string" - } - } - } - ], - "title": "Echo Process", - "version": "1.0.0" -} diff --git a/unity-test/test_data/process_descriptions/cwl_dag.json b/unity-test/test_data/process_descriptions/cwl_dag.json deleted file mode 100644 index a727cb2..0000000 --- a/unity-test/test_data/process_descriptions/cwl_dag.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "description": "This process accepts string input and echoes the input as a string output.", - "id": "cwl_dag", - "inputs": [ - { - "complexObjectInput": { - "description": "Excepts a string under message property.", - "schema": { - "properties": { - "message": { - "type": "string" - } - }, - "required": [ - "message" - ], - "type": "object" - }, - "title": "Input object" - } - } - ], - "jobControlOptions": [ - "async-execute", - "sync-execute" - ], - "links": [ - { - "href": "https://processing.example.org/oapi-p/processes/CWGDAGProcess/execution", - "rel": "http://www.opengis.net/def/rel/ogc/1.0/execute", - "title": "Execute endpoint" - } - ], - "outputs": [ - { - "stringOutput": { - "schema": { - "enum": [ - "Output string value" - ], - "type": "string" - } - } - } - ], - "title": "CWL DAG Process", - "version": "1.0.0" -} diff --git a/unity-test/test_data/process_descriptions/cwltool_help_dag.json b/unity-test/test_data/process_descriptions/cwltool_help_dag.json deleted file mode 100644 index b316a98..0000000 --- a/unity-test/test_data/process_descriptions/cwltool_help_dag.json +++ /dev/null @@ -1,360 +0,0 @@ -{ - "description": "This process accepts and number of input and simple echoes each input as an output.", - "id": "cwltool_help_dag", - "inputs": [ - { - "arrayInput": { - "description": "This is an example of a single process input that is an array of values. In this case, the input array would be interpreted as a single value and not as individual inputs.", - "schema": { - "items": { - "type": "integer" - }, - "maxItems": 10, - "minItems": 2, - "type": "array" - }, - "title": "Array Input Example" - }, - "boundingBoxInput": { - "description": "This is an example of a BBOX literal input.", - "schema": { - "allOf": [ - { - "format": "ogc-bbox" - }, - { - "$ref": "../../openapi/schemas/bbox.yaml" - } - ] - }, - "title": "Bounding Box Input Example" - }, - "complexObjectInput": { - "description": "This is an example of a complex object input.", - "schema": { - "properties": { - "property1": { - "type": "string" - }, - "property2": { - "format": "uri", - "type": "string" - }, - "property3": { - "type": "number" - }, - "property4": { - "format": "date-time", - "type": "string" - }, - "property5": { - "type": "boolean" - } - }, - "required": [ - "property1", - "property5" - ], - "type": "object" - }, - "title": "Complex Object Input Example" - }, - "dateInput": { - "description": "This is an example of a DATE literal input.", - "schema": { - "format": "date-time", - "type": "string" - }, - "title": "Date Literal Input Example" - }, - "doubleInput": { - "description": "This is an example of a DOUBLE literal input that is bounded between a value greater than 0 and 10. The default value is 5.", - "schema": { - "default": 5, - "exclusiveMinimum": true, - "format": "double", - "maximum": 10, - "minimum": 0, - "type": "number" - }, - "title": "Bounded Double Literal Input Example" - }, - "featureCollectionInput": { - "description": "This is an example of an input that is a feature collection that can be encoded in one of three ways: as a GeoJSON feature collection, as a GML feature collection retrieved from a WFS or as a KML document.", - "schema": { - "oneOf": [ - { - "contentMediaType": "application/gml+xml; version=3.2", - "type": "string" - }, - { - "contentMediaType": "application/vnd.google-earth.kml+xml", - "contentSchema": "https://schemas.opengis.net/kml/2.3/ogckml23.xsd", - "type": "string" - }, - { - "allOf": [ - { - "format": "geojson-feature-collection" - }, - { - "$ref": "https://geojson.org/schema/FeatureCollection.json" - } - ] - } - ] - }, - "title": "Feature Collection Input Example." - }, - "geometryInput": { - "description": "This is an example of a geometry input. In this case the geometry can be expressed as a GML of GeoJSON geometry.", - "maxOccurs": 5, - "minOccurs": 2, - "schema": { - "oneOf": [ - { - "contentMediaType": "application/gml+xml; version=3.2", - "contentSchema": "http://schemas.opengis.net/gml/3.2.1/geometryBasic2d.xsd", - "type": "string" - }, - { - "format": "geojson-geometry" - } - ] - }, - "title": "Geometry input" - }, - "imagesInput": { - "description": "This is an example of an image input. In this case, the input is an array of up to 150 images that might, for example, be a set of tiles. The oneOf[] conditional is used to indicate the acceptable image content types; GeoTIFF and JPEG 2000 in this case. Each input image in the input array can be included inline in the execute request as a base64-encoded string or referenced using the link.yaml schema. The use of a base64-encoded string is implied by the specification and does not need to be specified in the definition of the input.", - "maxOccurs": 150, - "minOccurs": 1, - "schema": { - "oneOf": [ - { - "contentEncoding": "binary", - "contentMediaType": "image/tiff; application=geotiff", - "type": "string" - }, - { - "contentEncoding": "binary", - "contentMediaType": "image/jp2", - "type": "string" - } - ] - }, - "title": "Inline Images Value Input" - }, - "measureInput": { - "description": "This is an example of a NUMERIC literal with an associated unit of measure.", - "schema": { - "properties": { - "measurement": { - "type": "number" - }, - "reference": { - "format": "uri", - "type": "string" - }, - "uom": { - "type": "string" - } - }, - "required": [ - "measurement", - "uom" - ], - "type": "object" - }, - "title": "Numerical Value with UOM Example" - }, - "stringInput": { - "description": "This is an example of a STRING literal input.", - "schema": { - "enum": [ - "Value1", - "Value2", - "Value3" - ], - "type": "string" - }, - "title": "String Literal Input Example" - } - } - ], - "jobControlOptions": [ - "async-execute", - "sync-execute" - ], - "links": [ - { - "href": "https://processing.example.org/oapi-p/processes/EchoProcess/execution", - "rel": "http://www.opengis.net/def/rel/ogc/1.0/execute", - "title": "Execute endpoint" - } - ], - "outputs": [ - { - "arrayOutput": { - "schema": { - "items": { - "type": "integer" - }, - "maxItems": 10, - "minItems": 2, - "type": "array" - } - }, - "boundingBoxOutput": { - "schema": { - "allOf": [ - { - "format": "ogc-bbox" - }, - { - "$ref": "../../openapi/schemas/bbox.yaml" - } - ] - } - }, - "complexObjectOutput": { - "schema": { - "properties": { - "property1": { - "type": "string" - }, - "property2": { - "format": "uri", - "type": "string" - }, - "property3": { - "type": "number" - }, - "property4": { - "format": "date-time", - "type": "string" - }, - "property5": { - "type": "boolean" - } - }, - "required": [ - "property1", - "property5" - ], - "type": "object" - } - }, - "dateOutput": { - "schema": { - "format": "date-time", - "type": "string" - } - }, - "doubleOutput": { - "schema": { - "default": 5, - "exclusiveMinimum": true, - "format": "double", - "maximum": 10, - "minimum": 0, - "type": "number" - } - }, - "featureCollectionOutput": { - "schema": { - "oneOf": [ - { - "contentMediaType": "application/gml+xml; version=3.2", - "type": "string" - }, - { - "contentMediaType": "application/vnd.google-earth.kml+xml", - "contentSchema": "https://schemas.opengis.net/kml/2.3/ogckml23.xsd", - "type": "string" - }, - { - "allOf": [ - { - "format": "geojson-feature-collection" - }, - { - "$ref": "https://geojson.org/schema/FeatureCollection.json" - } - ] - } - ] - } - }, - "geometryOutput": { - "schema": { - "oneOf": [ - { - "contentMediaType": "application/gml+xml", - "contentSchema": "http://schemas.opengis.net/gml/3.2.1/geometryBasic2d.xsd", - "type": "string" - }, - { - "allOf": [ - { - "format": "geojson-geometry" - }, - { - "$ref": "http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/schemas/geometryGeoJSON.yaml" - } - ] - } - ] - } - }, - "imagesOutput": { - "schema": { - "oneOf": [ - { - "contentEncoding": "binary", - "contentMediaType": "image/tiff; application=geotiff", - "type": "string" - }, - { - "contentEncoding": "binary", - "contentMediaType": "image/jp2", - "type": "string" - } - ] - } - }, - "measureOutput": { - "schema": { - "properties": { - "measurement": { - "type": "number" - }, - "reference": { - "format": "uri", - "type": "string" - }, - "uom": { - "type": "string" - } - }, - "required": [ - "measurement", - "uom" - ], - "type": "object" - } - }, - "stringOutput": { - "schema": { - "enum": [ - "Value1", - "Value2", - "Value3" - ], - "type": "string" - } - } - } - ], - "title": "Echo Process", - "version": "1.0.0" -} diff --git a/unity-test/test_main.py b/unity-test/test_main.py deleted file mode 100644 index a671043..0000000 --- a/unity-test/test_main.py +++ /dev/null @@ -1,164 +0,0 @@ -import glob -import json -import os -import pathlib - -import pytest -from fastapi import status -from fastapi.encoders import jsonable_encoder - -from app.schemas.ogc_processes import ( - ConfClasses, - Execute, - JobList, - LandingPage, - Process, - ProcessList, - Results, - StatusCode, - StatusInfo, -) -from app.schemas.unity_sps import HealthCheck - -TEST_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_data") -PROCESS_FILES = glob.glob(f"{TEST_DIR}/process_descriptions/*.json") -EXECUTION_FILES = glob.glob(f"{TEST_DIR}/execution_requests/*.json") - - -def test_get_landing_page(client): - response = client.get("/") - assert response.status_code == status.HTTP_200_OK - data = response.json() - landing_page = LandingPage.model_validate(data) - assert landing_page.title == "Unity SPS Processing Server" - - -def test_get_health(client): - response = client.get("/health") - assert response.status_code == status.HTTP_200_OK - data = response.json() - landing_page = HealthCheck.model_validate(data) - assert landing_page.status == "OK" - - -def test_get_conformance_declaration(client): - response = client.get("/conformance") - assert response.status_code == status.HTTP_200_OK - data = response.json() - conformance_declaration = ConfClasses.model_validate(data) - assert len(conformance_declaration.conformsTo) > 0 - assert conformance_declaration.conformsTo == [ - "http://www.opengis.net/spec/ogcapi-processes-1/1.0/conf/ogc-process-description" - ] - - -def test_delete_dismiss_execution(test_directory, client, deploy_process): - data_filename = os.path.join(test_directory, f"test_data/execution_requests/{deploy_process.id}.json") - f = open(data_filename) - execute_json = json.load(f) - execute = Execute.model_validate(execute_json) - response = client.post(f"/processes/{deploy_process.id}/execution", json=jsonable_encoder(execute)) - data = response.json() - status_info = StatusInfo.model_validate(data) - - response = client.delete(f"/jobs/{status_info.jobID}") - assert response.status_code == status.HTTP_200_OK - data = response.json() - status_info = StatusInfo.model_validate(data) - assert status_info.status == StatusCode.dismissed.value - - -def test_get_process_description(client, deploy_process): - response = client.get(f"/processes/{deploy_process.id}") - assert response.status_code == status.HTTP_200_OK - data = response.json() - process = Process.model_validate(data) - assert process.id == deploy_process.id - - -def test_get_process_list(client): - response = client.get("/processes") - assert response.status_code == status.HTTP_200_OK - data = response.json() - assert ProcessList.model_validate(data) - - -def test_get_job_list(client): - response = client.get("/jobs") - assert response.status_code == status.HTTP_200_OK - data = response.json() - assert JobList.model_validate(data) - - -def test_get_status(client, execute_process): - response = client.get(f"/jobs/{execute_process.jobID}") - assert response.status_code == status.HTTP_200_OK - data = response.json() - status_info = StatusInfo.model_validate(data) - assert status_info.jobID == execute_process.jobID - - -def test_get_results(client, execute_process): - response = client.get(f"/jobs/{execute_process.jobID}/results") - assert response.status_code == status.HTTP_200_OK - data = response.json() - assert Results.model_validate(data) - - -@pytest.mark.parametrize("process_filename", PROCESS_FILES) -def test_post_deploy_process_dag(test_directory, client, process_filename): - f = open(process_filename) - process_json = json.load(f) - process = Process.model_validate(process_json) - response = client.post("/processes", json=process.model_dump()) - assert response.status_code == status.HTTP_200_OK - data = response.json() - process = Process.model_validate(data) - assert process.id == pathlib.Path(process_filename).stem - - -@pytest.mark.dependency(name="test_post_execute_process_dag") -@pytest.mark.parametrize("execution_filename", EXECUTION_FILES) -def test_post_execute_process_dag(test_directory, client, execution_filename): - f = open(execution_filename) - execute_json = json.load(f) - execute = Execute.model_validate(execute_json) - process_id = pathlib.Path(execution_filename).stem - response = client.post(f"/processes/{process_id}/execution", json=jsonable_encoder(execute)) - assert response.status_code == status.HTTP_200_OK - data = response.json() - assert StatusInfo.model_validate(data) - assert data["processID"] == process_id - assert data["type"] == "process" - - -@pytest.mark.parametrize("execution_filename", EXECUTION_FILES) -def test_get_status_dag(test_directory, client, execution_filename): - f = open(execution_filename) - execute_json = json.load(f) - execute = Execute.model_validate(execute_json) - process_id = pathlib.Path(execution_filename).stem - execute_response = client.post(f"/processes/{process_id}/execution", json=jsonable_encoder(execute)) - data = execute_response.json() - status_info = StatusInfo.model_validate(data) - job_id = status_info.jobID - - code = status.HTTP_404_NOT_FOUND - while code != status.HTTP_200_OK: - status_response = client.get(f"/jobs/{job_id}") - code = status_response.status_code - - assert status_response.status_code == status.HTTP_200_OK - data = status_response.json() - status_info = StatusInfo.model_validate(data) - assert status_info.jobID == job_id - - -@pytest.mark.parametrize("process_filename", PROCESS_FILES) -@pytest.mark.dependency(depends=["test_post_execute_process_dag"]) -def test_delete_undeploy_dag(client, process_filename): - process_id = pathlib.Path(process_filename).stem - response = client.delete(f"/processes/{process_id}") - assert response.status_code == status.HTTP_409_CONFLICT - response = client.delete(f"/processes/{process_id}", params={"force": True}) - assert response.status_code == status.HTTP_204_NO_CONTENT From 98230c4538e369b22ad3c3a26fc243f9bba62648 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 17 Sep 2024 16:54:47 -0700 Subject: [PATCH 59/60] chore: Fix static analysis issues --- .github/workflows/unit_tests.yml | 4 +- app/pyproject.toml | 11 +- unity-test/conftest.py | 493 ---------------------------- unity-test/echoProcess.json | 73 ---- unity-test/test_api_api.py | 39 --- unity-test/test_conformance_api.py | 22 -- unity-test/test_dru_api.py | 73 ---- unity-test/test_jobs_api.py | 75 ----- unity-test/test_landing_page_api.py | 22 -- unity-test/test_processes_api.py | 64 ---- 10 files changed, 12 insertions(+), 864 deletions(-) delete mode 100644 unity-test/conftest.py delete mode 100644 unity-test/echoProcess.json delete mode 100644 unity-test/test_api_api.py delete mode 100644 unity-test/test_conformance_api.py delete mode 100644 unity-test/test_dru_api.py delete mode 100644 unity-test/test_jobs_api.py delete mode 100644 unity-test/test_landing_page_api.py delete mode 100644 unity-test/test_processes_api.py diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 9ce886e..23e2f3c 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -18,8 +18,8 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -e ".[test]" + pip install -e "app/.[test]" - name: Run unit tests run: | - pytest -vv + pytest -vv app/tests diff --git a/app/pyproject.toml b/app/pyproject.toml index e57b9df..67648d3 100644 --- a/app/pyproject.toml +++ b/app/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "unity-sps-ogc-processes-api" -version = "1.0.0" +version = "2.0.0" authors = [ { name = "Drew Meyers", email = "drew.meyers@jpl.nasa.gov" }, ] @@ -31,6 +31,15 @@ dependencies = [ "apache-airflow-client @ git+https://github.com/apache/airflow-client-python.git@2.9.0" ] +[project.optional-dependencies] +test = [ + "pytest==8.0.2", + "pytest-dependency==0.6.0", + "httpx==0.23.0", + "requests-mock==1.12.1", + "pyfakefs==5.4.1", + "fakeredis[lua]==2.23.1" +] [tool.setuptools.packages.find] where = ["src"] diff --git a/unity-test/conftest.py b/unity-test/conftest.py deleted file mode 100644 index c265749..0000000 --- a/unity-test/conftest.py +++ /dev/null @@ -1,493 +0,0 @@ -import json -import os -import re - -import fakeredis -import pytest -from fastapi import status - -# from fastapi.encoders import jsonable_encoder -from fastapi.testclient import TestClient -from openapi_server.database import Base -from openapi_server.database.models import Process -from openapi_server.utils.redis import RedisLock -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker -from sqlalchemy.pool import StaticPool -from unity_sps_ogc_processes_api.dependencies import get_db, get_redis_locking_client, get_settings -from unity_sps_ogc_processes_api.main import app - -settings = get_settings() -SQLALCHEMY_DATABASE_URL = settings.DB_URL -engine = create_engine( - SQLALCHEMY_DATABASE_URL, - connect_args={"check_same_thread": False}, - poolclass=StaticPool, -) -TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) -Base.metadata.create_all(bind=engine) - - -def override_get_db(): - try: - db = TestingSessionLocal() - yield db - finally: - db.close() - - -def override_get_redis_locking_client(): - redis_client = fakeredis.FakeRedis(decode_responses=True, version=(6,)) - return RedisLock(redis_client) - - -app.dependency_overrides[get_db] = override_get_db -app.dependency_overrides[get_redis_locking_client] = override_get_redis_locking_client - - -@pytest.fixture(scope="session") -def test_directory(): - """Returns the directory path of the current test session.""" - return os.path.dirname(os.path.abspath(__file__)) - - -@pytest.fixture(scope="session", autouse=True) -def fake_filesystem(fs_session, test_directory): # pylint:disable=invalid-name - """Variable name 'fs' causes a pylint warning. Provide a longer name - acceptable to pylint for use in tests. - """ - fs_session.add_real_directory(os.path.join(test_directory, "..", "process_descriptions")) - fs_session.add_real_directory(os.path.join(test_directory), "test_data") - fs_session.create_dir(settings.DAG_CATALOG_DIRECTORY) - fs_session.create_file( - os.path.join(settings.DAG_CATALOG_DIRECTORY, "cwltool_help_dag.py"), - contents="test", - ) - fs_session.create_file(os.path.join(settings.DAG_CATALOG_DIRECTORY, "EchoProcess.py"), contents="test") - fs_session.create_dir(settings.DEPLOYED_DAGS_DIRECTORY) - yield fs_session - - -@pytest.fixture(scope="session") -def client(): - return TestClient(app) - - -@pytest.fixture(scope="function", autouse=True) -def mock_get_existing_dag(requests_mock): - return requests_mock.get( - re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)$"), - [ - { - "json": { - "dag_id": "string", - "dag_display_name": "string", - "root_dag_id": "string", - "is_paused": False, - "is_active": True, - "is_subdag": True, - "last_parsed_time": "2019-08-24T14:15:22.548326+00:00", - "last_pickled": "2019-08-24T14:15:22.548326+00:00", - "last_expired": "2019-08-24T14:15:22.548326+00:00", - "scheduler_lock": True, - "pickle_id": "string", - "default_view": "string", - "fileloc": "string", - "file_token": "string", - "owners": ["string"], - "description": "string", - "schedule_interval": { - "__type": "string", - "days": 0, - "seconds": 0, - "microseconds": 0, - }, - "timetable_description": "string", - "tags": [{"name": "string"}], - "max_active_tasks": 0, - "max_active_runs": 0, - "has_task_concurrency_limits": True, - "has_import_errors": True, - "next_dagrun": "2019-08-24T14:15:22.548326+00:00", - "next_dagrun_data_interval_start": "2019-08-24T14:15:22.548326+00:00", - "next_dagrun_data_interval_end": "2019-08-24T14:15:22.548326+00:00", - "next_dagrun_create_after": "2019-08-24T14:15:22.548326+00:00", - } - }, - { - "json": { - "dag_id": "string", - "dag_display_name": "string", - "root_dag_id": "string", - "is_paused": True, - "is_active": False, - "is_subdag": True, - "last_parsed_time": "2019-08-24T14:15:22.548326+00:00", - "last_pickled": "2019-08-24T14:15:22.548326+00:00", - "last_expired": "2019-08-24T14:15:22.548326+00:00", - "scheduler_lock": True, - "pickle_id": "string", - "default_view": "string", - "fileloc": "string", - "file_token": "string", - "owners": ["string"], - "description": "string", - "schedule_interval": { - "__type": "string", - "days": 0, - "seconds": 0, - "microseconds": 0, - }, - "timetable_description": "string", - "tags": [{"name": "string"}], - "max_active_tasks": 0, - "max_active_runs": 0, - "has_task_concurrency_limits": True, - "has_import_errors": True, - "next_dagrun": "2019-08-24T14:15:22.548326+00:00", - "next_dagrun_data_interval_start": "2019-08-24T14:15:22.548326+00:00", - "next_dagrun_data_interval_end": "2019-08-24T14:15:22.548326+00:00", - "next_dagrun_create_after": "2019-08-24T14:15:22.548326+00:00", - } - }, - ], - ) - - -@pytest.fixture(scope="function", autouse=True) -def mock_patch_existing_dag(requests_mock): - return requests_mock.patch( - re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)$"), - [ - { - "json": { - "dag_id": "string", - "dag_display_name": "string", - "root_dag_id": "string", - "is_paused": False, - "is_active": True, - "is_subdag": True, - "last_parsed_time": "2019-08-24T14:15:22Z", - "last_pickled": "2019-08-24T14:15:22Z", - "last_expired": "2019-08-24T14:15:22Z", - "scheduler_lock": True, - "pickle_id": "string", - "default_view": "string", - "fileloc": "string", - "file_token": "string", - "owners": ["string"], - "description": "string", - "schedule_interval": { - "__type": "string", - "days": 0, - "seconds": 0, - "microseconds": 0, - }, - "timetable_description": "string", - "tags": [{"name": "string"}], - "max_active_tasks": 0, - "max_active_runs": 0, - "has_task_concurrency_limits": True, - "has_import_errors": True, - "next_dagrun": "2019-08-24T14:15:22Z", - "next_dagrun_data_interval_start": "2019-08-24T14:15:22Z", - "next_dagrun_data_interval_end": "2019-08-24T14:15:22Z", - "next_dagrun_create_after": "2019-08-24T14:15:22Z", - } - }, - { - "json": { - "dag_id": "string", - "dag_display_name": "string", - "root_dag_id": "string", - "is_paused": True, - "is_active": False, - "is_subdag": True, - "last_parsed_time": "2019-08-24T14:15:22Z", - "last_pickled": "2019-08-24T14:15:22Z", - "last_expired": "2019-08-24T14:15:22Z", - "scheduler_lock": True, - "pickle_id": "string", - "default_view": "string", - "fileloc": "string", - "file_token": "string", - "owners": ["string"], - "description": "string", - "schedule_interval": { - "__type": "string", - "days": 0, - "seconds": 0, - "microseconds": 0, - }, - "timetable_description": "string", - "tags": [{"name": "string"}], - "max_active_tasks": 0, - "max_active_runs": 0, - "has_task_concurrency_limits": True, - "has_import_errors": True, - "next_dagrun": "2019-08-24T14:15:22Z", - "next_dagrun_data_interval_start": "2019-08-24T14:15:22Z", - "next_dagrun_data_interval_end": "2019-08-24T14:15:22Z", - "next_dagrun_create_after": "2019-08-24T14:15:22Z", - } - }, - ], - ) - - -@pytest.fixture(scope="function", autouse=True) -def mock_delete_existing_dag(requests_mock): - return requests_mock.delete( - re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)$"), - status_code=status.HTTP_204_NO_CONTENT, - ) - - -@pytest.fixture(scope="function", autouse=True) -def mock_post_existing_dag_new_dagrun(requests_mock): - return requests_mock.post( - re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns$"), - json={ - "dag_run_id": "string", - "logical_date": "2019-08-24T14:15:22Z", - "execution_date": "2019-08-24T14:15:22Z", - "data_interval_start": "2019-08-24T14:15:22Z", - "data_interval_end": "2019-08-24T14:15:22Z", - "conf": {}, - "note": "string", - }, - ) - - -@pytest.fixture(scope="function", autouse=True) -def mock_get_existing_dag_dagruns(requests_mock): - return requests_mock.get( - re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns$"), - json={ - "dag_runs": [ - { - "dag_run_id": "string", - "dag_id": "string", - "logical_date": "2019-08-24T14:15:22.548326+00:00", - "execution_date": "2019-08-24T14:15:22.548326+00:00", - "start_date": "2019-08-24T14:15:22.548326+00:00", - "end_date": "2019-08-24T14:15:22.548326+00:00", - "data_interval_start": "2019-08-24T14:15:22.548326+00:00", - "data_interval_end": "2019-08-24T14:15:22.548326+00:00", - "last_scheduling_decision": "2019-08-24T14:15:22.548326+00:00", - "run_type": "backfill", - "state": "queued", - "external_trigger": True, - "conf": {}, - "note": "string", - } - ], - "total_entries": 1, - }, - ) - - -@pytest.fixture(scope="function", autouse=True) -def mock_get_existing_running_dag_dagruns(requests_mock): - return requests_mock.get( - re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns\\?state=running$"), - json={ - "dag_runs": [ - { - "dag_run_id": "string", - "dag_id": "string", - "logical_date": "2019-08-24T14:15:22.548326+00:00", - "execution_date": "2019-08-24T14:15:22.548326+00:00", - "start_date": "2019-08-24T14:15:22.548326+00:00", - "end_date": "2019-08-24T14:15:22.548326+00:00", - "data_interval_start": "2019-08-24T14:15:22.548326+00:00", - "data_interval_end": "2019-08-24T14:15:22.548326+00:00", - "last_scheduling_decision": "2019-08-24T14:15:22.548326+00:00", - "run_type": "backfill", - "state": "running", - "external_trigger": True, - "conf": {}, - "note": "string", - } - ], - "total_entries": 1, - }, - ) - - -@pytest.fixture(scope="function", autouse=True) -def mock_patch_existing_running_dag_dagrun(requests_mock): - return requests_mock.patch( - re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)$"), - json={ - "dag_run_id": "string", - "dag_id": "string", - "logical_date": "2019-08-24T14:15:22.548326+00:00", - "execution_date": "2019-08-24T14:15:22.548326+00:00", - "start_date": "2019-08-24T14:15:22.548326+00:00", - "end_date": "2019-08-24T14:15:22.548326+00:00", - "data_interval_start": "2019-08-24T14:15:22.548326+00:00", - "data_interval_end": "2019-08-24T14:15:22.548326+00:00", - "last_scheduling_decision": "2019-08-24T14:15:22.548326+00:00", - "run_type": "backfill", - "state": "queued", - "external_trigger": True, - "conf": {}, - "note": "string", - }, - ) - - -@pytest.fixture(scope="function", autouse=True) -def mock_get_existing_dag_dagrun(requests_mock): - return requests_mock.get( - re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)$"), - json={ - "dag_run_id": "string", - "dag_id": "string", - "logical_date": "2019-08-24T14:15:22.548326+00:00", - "execution_date": "2019-08-24T14:15:22.548326+00:00", - "start_date": "2019-08-24T14:15:22.548326+00:00", - "end_date": "2019-08-24T14:15:22.548326+00:00", - "data_interval_start": "2019-08-24T14:15:22.548326+00:00", - "data_interval_end": "2019-08-24T14:15:22.548326+00:00", - "last_scheduling_decision": "2019-08-24T14:15:22.548326+00:00", - "run_type": "backfill", - "state": "queued", - "external_trigger": True, - "conf": {}, - "note": "string", - }, - ) - - -@pytest.fixture(scope="function", autouse=True) -def mock_delete_existing_dag_dagrun(requests_mock): - return requests_mock.delete( - re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)$"), - status_code=status.HTTP_204_NO_CONTENT, - ) - - -@pytest.fixture(scope="function", autouse=True) -def mock_get_existing_running_dag_dagrun_tasks(requests_mock): - return requests_mock.get( - re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)/taskInstances$"), - json={ - "task_instances": [ - { - "task_id": "string", - "task_display_name": "string", - "dag_id": "string", - "dag_run_id": "string", - "execution_date": "string", - "start_date": "string", - "end_date": "string", - "duration": 0, - "state": None, - "try_number": 0, - "map_index": 0, - "max_tries": 0, - "hostname": "string", - "unixname": "string", - "pool": "string", - "pool_slots": 0, - "queue": "string", - "priority_weight": 0, - "operator": "string", - "queued_when": "string", - "pid": 0, - "executor_config": "string", - "sla_miss": { - "task_id": "string", - "dag_id": "string", - "execution_date": "string", - "email_sent": True, - "timestamp": "string", - "description": "string", - "notification_sent": True, - }, - "rendered_map_index": "string", - "rendered_fields": {}, - "trigger": { - "id": 0, - "classpath": "string", - "kwargs": "string", - "created_date": "string", - "triggerer_id": 0, - }, - "triggerer_job": { - "id": 0, - "dag_id": "string", - "state": "string", - "job_type": "string", - "start_date": "string", - "end_date": "string", - "latest_heartbeat": "string", - "executor_class": "string", - "hostname": "string", - "unixname": "string", - }, - "note": "string", - } - ], - "total_entries": 0, - }, - ) - - -@pytest.fixture(scope="function", autouse=True) -def mock_patch_existing_running_dag_dagrun_task(requests_mock): - return requests_mock.patch( - re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)/taskInstances/([^/]*)$"), - json={ - "task_id": "string", - "dag_id": "string", - "execution_date": "string", - "dag_run_id": "string", - }, - ) - - -@pytest.fixture(scope="function") -def deploy_process(test_directory, client): - data_filename = os.path.join(test_directory, "..", "process_descriptions", "cwltool_help_dag.json") - f = open(data_filename) - process_json = json.load(f) - process = Process.model_validate(process_json) - response = client.post("/processes", json=process.model_dump()) - assert response.status_code == status.HTTP_200_OK - data = response.json() - process = Process.model_validate(data) - assert process.id == "cwltool_help_dag" - - yield process - - response = client.delete(f"/processes/{process.id}", params={"force": True}) - assert response.status_code == status.HTTP_204_NO_CONTENT - - -# @pytest.fixture(scope="function") -# def execute_process( -# test_directory, mock_post_existing_dag_new_dagrun, client, deploy_process -# ): -# data_filename = os.path.join( -# test_directory, "test_data/execution_requests/execute_cwltool_help_dag.json" -# ) -# f = open(data_filename) -# execute_json = json.load(f) -# execute = Execute.model_validate(execute_json) -# response = client.post( -# f"/processes/{deploy_process.id}/execution", json=jsonable_encoder(execute) -# ) -# assert response.status_code == status.HTTP_200_OK -# data = response.json() -# status_info = StatusInfo.model_validate(data) - -# yield status_info - -# response = client.delete(f"/jobs/{status_info.jobID}") -# assert response.status_code == status.HTTP_200_OK -# data = response.json() -# status_info = StatusInfo.model_validate(data) -# assert status_info.status == StatusCode.dismissed.value diff --git a/unity-test/echoProcess.json b/unity-test/echoProcess.json deleted file mode 100644 index 9d2405a..0000000 --- a/unity-test/echoProcess.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "executionUnit": { - "additional_properties": {}, - "config": { - "additional_properties": {} - }, - "deployment": "cloud", - "image": "example/image:latest", - "type": "docker" - }, - "processDescription": { - "description": "This process performs an example task.", - "id": "EchoProcess", - "inputs": { - "input1": { - "description": "Description of Input 1", - "maxOccurs": 1, - "minOccurs": 1, - "schema": { - "$ref": "#/components/schemas/ExampleSchema" - }, - "title": "Input 1 Title" - } - }, - "jobControlOptions": [ - "sync-execute", - "async-execute" - ], - "keywords": [ - "example", - "process", - "OGC" - ], - "links": [ - { - "href": "http://example.com/process", - "hreflang": "en", - "rel": "self", - "title": "Process Description", - "type": "application/json" - } - ], - "metadata": [ - { - "href": "http://example.com/metadata", - "hreflang": "en", - "rel": "related", - "title": "Example Metadata", - "type": "application/json" - } - ], - "outputs": { - "output1": { - "description": "Description of Output 1", - "schema": { - "deprecated": false, - "exclusiveMaximum": false, - "exclusiveMinimum": false, - "minItems": 0, - "minLength": 0, - "minProperties": 0, - "nullable": false, - "readOnly": false, - "uniqueItems": false, - "writeOnly": false - }, - "title": "Output 1 Title" - } - }, - "title": "Example Process Title", - "version": "1.0.0" - } -} diff --git a/unity-test/test_api_api.py b/unity-test/test_api_api.py deleted file mode 100644 index 52a504f..0000000 --- a/unity-test/test_api_api.py +++ /dev/null @@ -1,39 +0,0 @@ -# coding: utf-8 - -from fastapi.testclient import TestClient -from unity_sps_ogc_processes_api.models.enumeration import Enumeration # noqa: F401 -from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 - - -def test_get_api(client: TestClient): - """Test case for get_api - - Retrieve this API definition. - """ - # uncomment below to make a request - # response = client.request( - # "GET", - # "/api", - # headers=headers, - # params=params, - # ) - - # uncomment below to assert the status code of the HTTP response - # assert response.status_code == 200 - - -def test_get_api_processes(client: TestClient): - """Test case for get_api_processes - - Retrieve the list of processes available from this API implementation & deployment. - """ - # uncomment below to make a request - # response = client.request( - # "GET", - # "/api/processes-list", - # headers=headers, - # params=params, - # ) - - # uncomment below to assert the status code of the HTTP response - # assert response.status_code == 200 diff --git a/unity-test/test_conformance_api.py b/unity-test/test_conformance_api.py deleted file mode 100644 index 9e18c2d..0000000 --- a/unity-test/test_conformance_api.py +++ /dev/null @@ -1,22 +0,0 @@ -# coding: utf-8 - -from fastapi.testclient import TestClient -from unity_sps_ogc_processes_api.models.conf_classes import ConfClasses # noqa: F401 -from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 - - -def test_get_conformance(client: TestClient): - """Test case for get_conformance - - Retrieve the set of OGC API conformance classes that are supported by this service. - """ - # uncomment below to make a request - # response = client.request( - # "GET", - # "/conformance", - # headers=headers, - # params=params, - # ) - - # uncomment below to assert the status code of the HTTP response - # assert response.status_code == 200 diff --git a/unity-test/test_dru_api.py b/unity-test/test_dru_api.py deleted file mode 100644 index ffd5ea8..0000000 --- a/unity-test/test_dru_api.py +++ /dev/null @@ -1,73 +0,0 @@ -# coding: utf-8 - -import json -import os - -import pytest -from fastapi.testclient import TestClient -from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg - - -@pytest.fixture -def sample_ogcapppkg(): - # Get the current directory of the test file - current_dir = os.path.dirname(os.path.abspath(__file__)) - - # Construct the path to echoProcess.json - json_file_path = os.path.join(current_dir, "echoProcess.json") - - # Read the JSON file - with open(json_file_path, "r") as file: - data = json.load(file) - - return Ogcapppkg(**data) - - -def test_deploy(client: TestClient, sample_ogcapppkg): - """Test case for deploy""" - response = client.post( - "/processes", - json=sample_ogcapppkg.model_dump(exclude_none=True, by_alias=True), - ) - assert response.status_code == 201 - - -def test_replace(client: TestClient, sample_ogcapppkg): - """Test case for replace""" - process_id = "EchoProcess" - response = client.put( - f"/processes/{process_id}", - json=sample_ogcapppkg.model_dump(), - ) - assert response.status_code == 204 - - -def test_undeploy(client: TestClient): - """Test case for undeploy""" - process_id = "EchoProcess" - response = client.delete(f"/processes/{process_id}", params={"force": True}) - assert response.status_code == 204 - - -def test_deploy_conflict(client: TestClient, sample_ogcapppkg): - """Test case for deploy when process already exists""" - # First, deploy the process - response = client.post( - "/processes", - json=sample_ogcapppkg.model_dump(exclude_none=True, by_alias=True), - ) - # Try to deploy the same process again - response = client.post( - "/processes", - json=sample_ogcapppkg.model_dump(exclude_none=True, by_alias=True), - ) - assert response.status_code == 409 - assert "already exists" in response.json()["detail"] - - -def test_undeploy_not_found(client: TestClient): - """Test case for undeploy when process doesn't exist""" - process_id = "non_existent_process" - response = client.delete(f"/processes/{process_id}") - assert response.status_code == 404 - assert "not found" in response.json()["detail"] diff --git a/unity-test/test_jobs_api.py b/unity-test/test_jobs_api.py deleted file mode 100644 index 0fa6d22..0000000 --- a/unity-test/test_jobs_api.py +++ /dev/null @@ -1,75 +0,0 @@ -# coding: utf-8 - -from fastapi.testclient import TestClient -from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 -from unity_sps_ogc_processes_api.models.inline_or_ref_data import InlineOrRefData # noqa: F401 -from unity_sps_ogc_processes_api.models.job_list import JobList # noqa: F401 -from unity_sps_ogc_processes_api.models.status_info import StatusInfo # noqa: F401 - - -def test_dismiss(client: TestClient): - """Test case for dismiss - - cancel a job execution, remove a finished job - """ - - # uncomment below to make a request - # response = client.request( - # "DELETE", - # "/jobs/{jobId}".format(jobId='job_id_example'), - # headers=headers, - # ) - - # uncomment below to assert the status code of the HTTP response - # assert response.status_code == 200 - - -def test_get_jobs(client: TestClient): - """Test case for get_jobs - - retrieve the list of jobs. - """ - - # uncomment below to make a request - # response = client.request( - # "GET", - # "/jobs", - # headers=headers, - # ) - - # uncomment below to assert the status code of the HTTP response - # assert response.status_code == 200 - - -def test_get_result(client: TestClient): - """Test case for get_result - - retrieve the result(s) of a job - """ - - # uncomment below to make a request - # response = client.request( - # "GET", - # "/jobs/{jobId}/results".format(jobId='job_id_example'), - # headers=headers, - # ) - - # uncomment below to assert the status code of the HTTP response - # assert response.status_code == 200 - - -def test_get_status(client: TestClient): - """Test case for get_status - - retrieve the status of a job - """ - - # uncomment below to make a request - # response = client.request( - # "GET", - # "/jobs/{jobId}".format(jobId='job_id_example'), - # headers=headers, - # ) - - # uncomment below to assert the status code of the HTTP response - # assert response.status_code == 200 diff --git a/unity-test/test_landing_page_api.py b/unity-test/test_landing_page_api.py deleted file mode 100644 index cb2e455..0000000 --- a/unity-test/test_landing_page_api.py +++ /dev/null @@ -1,22 +0,0 @@ -# coding: utf-8 - -from fastapi.testclient import TestClient -from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 -from unity_sps_ogc_processes_api.models.landing_page import LandingPage # noqa: F401 - - -def test_get_landing_page(client: TestClient): - """Test case for get_landing_page - - Retrieve the OGC API landing page for this service. - """ - # uncomment below to make a request - # response = client.request( - # "GET", - # "/", - # headers=headers, - # params=params, - # ) - - # uncomment below to assert the status code of the HTTP response - # assert response.status_code == 200 diff --git a/unity-test/test_processes_api.py b/unity-test/test_processes_api.py deleted file mode 100644 index faac309..0000000 --- a/unity-test/test_processes_api.py +++ /dev/null @@ -1,64 +0,0 @@ -# coding: utf-8 - -from fastapi.testclient import TestClient -from unity_sps_ogc_processes_api.models.exception import Exception # noqa: F401 -from unity_sps_ogc_processes_api.models.execute200_response import Execute200Response # noqa: F401 -from unity_sps_ogc_processes_api.models.execute200_response1 import Execute200Response1 # noqa: F401 -from unity_sps_ogc_processes_api.models.execute_workflows import ExecuteWorkflows # noqa: F401 -from unity_sps_ogc_processes_api.models.process import Process # noqa: F401 -from unity_sps_ogc_processes_api.models.process_list import ProcessList # noqa: F401 -from unity_sps_ogc_processes_api.models.processes_list import ProcessesList # noqa: F401 -from unity_sps_ogc_processes_api.models.status_info import StatusInfo # noqa: F401 - - -def test_execute(client: TestClient): - """Test case for execute - - execute a process. - """ - ExecuteWorkflows() - # uncomment below to make a request - # response = client.request( - # "POST", - # "/processes/{processId}/execution".format(processId=unity_sps_ogc_processes_api.ProcessesList()), - # headers=headers, - # json=execute_workflows, - # params=params, - # ) - - # uncomment below to assert the status code of the HTTP response - # assert response.status_code == 200 - - -def test_get_process_description(client: TestClient): - """Test case for get_process_description - - retrieve a process description - """ - - # uncomment below to make a request - # response = client.request( - # "GET", - # "/processes/{processId}".format(processId=unity_sps_ogc_processes_api.ProcessesList()), - # headers=headers, - # ) - - # uncomment below to assert the status code of the HTTP response - # assert response.status_code == 200 - - -def test_get_processes(client: TestClient): - """Test case for get_processes - - retrieve the list of available processes - """ - - # uncomment below to make a request - # response = client.request( - # "GET", - # "/processes", - # headers=headers, - # ) - - # uncomment below to assert the status code of the HTTP response - # assert response.status_code == 200 From a06b518700ab2a9387af953c8eb7648c93f32ec2 Mon Sep 17 00:00:00 2001 From: Drew Meyers Date: Tue, 17 Sep 2024 16:57:49 -0700 Subject: [PATCH 60/60] chore: Fix static analysis issues --- app/src/openapi_server/database/crud.py | 4 +- app/src/openapi_server/impl/jobs_api.py | 29 ++++-- .../openapi_server/impl/landing_page_api.py | 4 +- .../apis/dru_api.py | 12 ++- .../models/bbox.py | 6 +- .../models/bbox1.py | 6 +- .../models/bbox_def_crs.py | 16 +++- .../models/bbox_processes.py | 6 +- .../models/collection_info.py | 30 ++++-- .../models/collections.py | 5 +- .../unity_sps_ogc_processes_api/models/crs.py | 8 +- .../models/crs_one_of.py | 28 ++++-- .../models/crs_one_of_one_of.py | 4 +- .../models/data_type.py | 8 +- .../models/execute.py | 14 ++- .../models/execute200_response.py | 4 +- .../models/execute200_response1.py | 4 +- .../models/execute_workflows.py | 14 ++- .../models/execute_workflows1.py | 14 ++- .../models/execution_unit.py | 8 +- .../models/extent.py | 8 +- .../models/extent_spatial.py | 13 ++- .../models/extent_spatial_grid_inner.py | 10 +- ...nt_spatial_grid_inner_coordinates_inner.py | 8 +- .../extent_spatial_grid_inner_resolution.py | 8 +- .../models/extent_temporal.py | 8 +- .../models/extent_temporal_grid.py | 8 +- .../models/extent_temporal_grid_resolution.py | 8 +- .../models/extent_uad.py | 8 +- .../models/feature_collection.py | 4 +- .../models/format.py | 4 +- .../models/format_schema.py | 8 +- .../models/geo_json_feature.py | 10 +- .../models/geo_json_feature_geometry.py | 32 +++++-- .../models/geo_json_feature_id.py | 8 +- .../models/geo_json_line_string.py | 4 +- .../models/geo_json_multi_line_string.py | 4 +- .../models/geo_json_multi_point.py | 8 +- .../models/geo_json_multi_polygon.py | 8 +- .../models/geo_json_point.py | 4 +- .../models/geo_json_polygon.py | 4 +- .../models/inline_or_ref_data.py | 4 +- .../models/inline_or_ref_data1.py | 24 +++-- .../models/inline_or_ref_data_workflows.py | 24 +++-- .../models/input.py | 8 +- .../models/input_description.py | 12 ++- .../input_description_all_of_max_occurs.py | 8 +- .../models/input_process.py | 14 ++- .../models/input_value.py | 12 ++- .../models/input_value1.py | 12 ++- .../models/input_value_no_object.py | 12 ++- .../models/input_value_no_object1.py | 12 ++- .../models/input_value_no_object_workflows.py | 20 +++- .../models/input_value_workflows.py | 12 ++- .../models/input_workflows1.py | 20 +++- .../models/metadata_one_of1.py | 4 +- .../models/metadata_one_of1_value.py | 8 +- .../models/ogcapppkg_array_inner.py | 16 +++- .../models/output.py | 8 +- .../models/output_description.py | 4 +- .../models/output_workflows.py | 6 +- .../models/output_workflows1.py | 6 +- .../models/process.py | 14 ++- .../models/process_summary.py | 4 +- .../models/qualified_input_value.py | 10 +- .../models/qualified_input_value1.py | 10 +- .../models/qualified_input_value_workflows.py | 8 +- .../models/schema1.py | 8 +- .../models/schema_one_of.py | 95 +++++++++++++++---- .../schema_one_of_additional_properties.py | 8 +- .../models/static_indicator.py | 8 +- .../models/status_info.py | 4 +- app/tests/conftest.py | 20 +++- 73 files changed, 634 insertions(+), 202 deletions(-) diff --git a/app/src/openapi_server/database/crud.py b/app/src/openapi_server/database/crud.py index 4244ab3..e88e249 100644 --- a/app/src/openapi_server/database/crud.py +++ b/app/src/openapi_server/database/crud.py @@ -8,7 +8,9 @@ def create_process(db: Session, ogcapppkg: Ogcapppkg): db_process = models.Process(**ogcapppkg.process_description.model_dump()) db_execution_unit = models.ExecutionUnit(**ogcapppkg.execution_unit.model_dump()) - db_ogcapppkg = models.Ogcapppkg(process=db_process, execution_unit=db_execution_unit) + db_ogcapppkg = models.Ogcapppkg( + process=db_process, execution_unit=db_execution_unit + ) db.add(db_ogcapppkg) db.commit() db.refresh(db_ogcapppkg) diff --git a/app/src/openapi_server/impl/jobs_api.py b/app/src/openapi_server/impl/jobs_api.py index 2d7845d..a16ad0c 100644 --- a/app/src/openapi_server/impl/jobs_api.py +++ b/app/src/openapi_server/impl/jobs_api.py @@ -21,7 +21,9 @@ class JobsApiImpl(BaseJobsApi): - def __init__(self, settings: Settings, redis_locking_client: RedisLock, db: Session): + def __init__( + self, settings: Settings, redis_locking_client: RedisLock, db: Session + ): self.settings = settings self.redis_locking_client = redis_locking_client self.db = db @@ -92,7 +94,9 @@ def dismiss(self, jobId: str) -> StatusInfo: detail=f"Failed to delete DAG run {job.jobID} for DAG {job.processID}: {e}", ) except Exception as e: - raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) def get_jobs(self) -> JobList: jobs = crud.get_jobs(self.db) @@ -125,7 +129,10 @@ def get_result(self, jobId: str, prefer: str) -> Dict[str, InlineOrRefData]: process_lock_key = f"process:{job.processID}" with self.redis_locking_client.lock(process_lock_key): results = crud.get_results(self.db, jobId) - return {result.name: InlineOrRefData(href=result.href) for result in results} + return { + result.name: InlineOrRefData(href=result.href) + for result in results + } except LockError: raise HTTPException( @@ -133,7 +140,9 @@ def get_result(self, jobId: str, prefer: str) -> Dict[str, InlineOrRefData]: detail="Unable to acquire lock. Please try again later.", ) except Exception as e: - raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) def get_status(self, jobId: str) -> StatusInfo: job_lock_key = f"job:{jobId}" @@ -169,7 +178,9 @@ def get_status(self, jobId: str) -> StatusInfo: "failed": StatusCode.FAILED, } data = response.json() - current_execution_status = execution_status_conversion_dict[data["state"]] + current_execution_status = execution_status_conversion_dict[ + data["state"] + ] if job.status != current_execution_status: job.status = current_execution_status job.updated = datetime.now() @@ -178,7 +189,9 @@ def get_status(self, jobId: str) -> StatusInfo: if end_date_str: job.finished = datetime.fromisoformat(end_date_str) - return crud.update_job(self.db, job.job_id, job.model_dump(by_alias=True)) + return crud.update_job( + self.db, job.job_id, job.model_dump(by_alias=True) + ) except LockError: raise HTTPException( @@ -191,4 +204,6 @@ def get_status(self, jobId: str) -> StatusInfo: detail=f"Failed to fetch DAG run {job.job_id} for DAG {job.process_id}: {e}", ) except Exception as e: - raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) diff --git a/app/src/openapi_server/impl/landing_page_api.py b/app/src/openapi_server/impl/landing_page_api.py index d3ce484..721d2d6 100644 --- a/app/src/openapi_server/impl/landing_page_api.py +++ b/app/src/openapi_server/impl/landing_page_api.py @@ -9,7 +9,9 @@ def get_landing_page(self, f: str = None) -> LandingPage: title="OGC API - Processes", description="This is the landing page for the OGC API - Processes implementation.", links=[ - Link(href="/", rel="self", type="application/json", title="This document"), + Link( + href="/", rel="self", type="application/json", title="This document" + ), Link( href="/api", # Assuming the API definition is at /api rel="service-desc", diff --git a/app/src/unity_sps_ogc_processes_api/apis/dru_api.py b/app/src/unity_sps_ogc_processes_api/apis/dru_api.py index deb99f7..de701fa 100644 --- a/app/src/unity_sps_ogc_processes_api/apis/dru_api.py +++ b/app/src/unity_sps_ogc_processes_api/apis/dru_api.py @@ -44,7 +44,9 @@ async def deploy( settings: Settings = Depends(get_settings), redis_locking_client: RedisLock = Depends(get_redis_locking_client), db: Session = Depends(get_db), - ogcapppkg: Ogcapppkg = Body(None, description="An OGC Application Package used to deploy a new process."), + ogcapppkg: Ogcapppkg = Body( + None, description="An OGC Application Package used to deploy a new process." + ), w: str = Query( None, description="Point to the workflow identifier for deploying a CWL containing multiple workflow definitions", @@ -80,7 +82,9 @@ async def replace( redis_locking_client: RedisLock = Depends(get_redis_locking_client), db: Session = Depends(get_db), processId: str = Path(..., description=""), - ogcapppkg: Ogcapppkg = Body(None, description="An OGC Application Package used to deploy a new process."), + ogcapppkg: Ogcapppkg = Body( + None, description="An OGC Application Package used to deploy a new process." + ), ) -> None: """Replaces a process. For more information, see [Section 6.4](http://docs.ogc.org/DRAFTS/20-044.html#_18582f42-ebc6-4284-9333-c089068f62b6).""" dru_api = BaseDRUApi.subclasses[0](settings, redis_locking_client, db) @@ -111,7 +115,9 @@ async def undeploy( redis_locking_client: RedisLock = Depends(get_redis_locking_client), db: Session = Depends(get_db), processId: str = Path(..., description=""), - force: bool = Query(False, description="Force undeployment even if there are active DAG runs"), + force: bool = Query( + False, description="Force undeployment even if there are active DAG runs" + ), ) -> None: """Undeploys a process. For more information, see [Section 6.5](http://docs.ogc.org/DRAFTS/20-044.html#_16391f9e-538f-4a84-9710-72a6bab82842).""" dru_api = BaseDRUApi.subclasses[0](settings, redis_locking_client, db) diff --git a/app/src/unity_sps_ogc_processes_api/models/bbox.py b/app/src/unity_sps_ogc_processes_api/models/bbox.py index 8a1d958..2c49ce6 100644 --- a/app/src/unity_sps_ogc_processes_api/models/bbox.py +++ b/app/src/unity_sps_ogc_processes_api/models/bbox.py @@ -91,7 +91,11 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { "bbox": obj.get("bbox"), - "crs": (BboxDefCrs.from_dict(obj.get("crs")) if obj.get("crs") is not None else None), + "crs": ( + BboxDefCrs.from_dict(obj.get("crs")) + if obj.get("crs") is not None + else None + ), } ) return _obj diff --git a/app/src/unity_sps_ogc_processes_api/models/bbox1.py b/app/src/unity_sps_ogc_processes_api/models/bbox1.py index 0da5f62..c85ff66 100644 --- a/app/src/unity_sps_ogc_processes_api/models/bbox1.py +++ b/app/src/unity_sps_ogc_processes_api/models/bbox1.py @@ -91,7 +91,11 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { "bbox": obj.get("bbox"), - "crs": (BboxDefCrs.from_dict(obj.get("crs")) if obj.get("crs") is not None else None), + "crs": ( + BboxDefCrs.from_dict(obj.get("crs")) + if obj.get("crs") is not None + else None + ), } ) return _obj diff --git a/app/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py b/app/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py index d230477..9c415b7 100644 --- a/app/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py +++ b/app/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py @@ -37,9 +37,13 @@ class BboxDefCrs(BaseModel): """ # data type: str - anyof_schema_1_validator: Optional[StrictStr] = "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + anyof_schema_1_validator: Optional[StrictStr] = ( + "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + ) # data type: str - anyof_schema_2_validator: Optional[StrictStr] = "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + anyof_schema_2_validator: Optional[StrictStr] = ( + "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + ) if TYPE_CHECKING: actual_instance: Optional[Union[str]] = None else: @@ -54,9 +58,13 @@ class BboxDefCrs(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app/src/unity_sps_ogc_processes_api/models/bbox_processes.py b/app/src/unity_sps_ogc_processes_api/models/bbox_processes.py index ac1d79b..0f5a692 100644 --- a/app/src/unity_sps_ogc_processes_api/models/bbox_processes.py +++ b/app/src/unity_sps_ogc_processes_api/models/bbox_processes.py @@ -91,7 +91,11 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { "bbox": obj.get("bbox"), - "crs": (BboxDefCrs.from_dict(obj.get("crs")) if obj.get("crs") is not None else None), + "crs": ( + BboxDefCrs.from_dict(obj.get("crs")) + if obj.get("crs") is not None + else None + ), } ) return _obj diff --git a/app/src/unity_sps_ogc_processes_api/models/collection_info.py b/app/src/unity_sps_ogc_processes_api/models/collection_info.py index 41e5f65..72bece4 100644 --- a/app/src/unity_sps_ogc_processes_api/models/collection_info.py +++ b/app/src/unity_sps_ogc_processes_api/models/collection_info.py @@ -40,8 +40,12 @@ class CollectionInfo(BaseModel): CollectionInfo """ # noqa: E501 - id: StrictStr = Field(description="identifier of the collection used, for example, in URIs") - title: Optional[StrictStr] = Field(default=None, description="human readable title of the collection") + id: StrictStr = Field( + description="identifier of the collection used, for example, in URIs" + ) + title: Optional[StrictStr] = Field( + default=None, description="human readable title of the collection" + ) description: Optional[StrictStr] = Field( default=None, description="a description of the data in the collection" ) @@ -57,10 +61,12 @@ class CollectionInfo(BaseModel): description="the list of coordinate reference systems supported by the API; the first item is the default coordinate reference system", ) data_type: Optional[CollectionInfoDataType] = Field(default=None, alias="dataType") - geometry_dimension: Optional[Annotated[int, Field(le=3, strict=True, ge=0)]] = Field( - default=None, - description="The geometry dimension of the features shown in this layer (0: points, 1: curves, 2: surfaces, 3: solids), unspecified: mixed or unknown", - alias="geometryDimension", + geometry_dimension: Optional[Annotated[int, Field(le=3, strict=True, ge=0)]] = ( + Field( + default=None, + description="The geometry dimension of the features shown in this layer (0: points, 1: curves, 2: surfaces, 3: solids), unspecified: mixed or unknown", + alias="geometryDimension", + ) ) min_scale_denominator: Optional[Union[StrictFloat, StrictInt]] = Field( default=None, @@ -167,8 +173,16 @@ def from_dict(cls, obj: Dict) -> Self: if obj.get("links") is not None else None ), - "extent": (ExtentUad.from_dict(obj.get("extent")) if obj.get("extent") is not None else None), - "itemType": (obj.get("itemType") if obj.get("itemType") is not None else "unknown"), + "extent": ( + ExtentUad.from_dict(obj.get("extent")) + if obj.get("extent") is not None + else None + ), + "itemType": ( + obj.get("itemType") + if obj.get("itemType") is not None + else "unknown" + ), "crs": obj.get("crs"), "dataType": ( CollectionInfoDataType.from_dict(obj.get("dataType")) diff --git a/app/src/unity_sps_ogc_processes_api/models/collections.py b/app/src/unity_sps_ogc_processes_api/models/collections.py index 2115561..4dd6775 100644 --- a/app/src/unity_sps_ogc_processes_api/models/collections.py +++ b/app/src/unity_sps_ogc_processes_api/models/collections.py @@ -126,7 +126,10 @@ def from_dict(cls, obj: Dict) -> Self: "numberMatched": obj.get("numberMatched"), "numberReturned": obj.get("numberReturned"), "collections": ( - [CollectionInfo.from_dict(_item) for _item in obj.get("collections")] + [ + CollectionInfo.from_dict(_item) + for _item in obj.get("collections") + ] if obj.get("collections") is not None else None ), diff --git a/app/src/unity_sps_ogc_processes_api/models/crs.py b/app/src/unity_sps_ogc_processes_api/models/crs.py index 068b6ec..47a3fab 100644 --- a/app/src/unity_sps_ogc_processes_api/models/crs.py +++ b/app/src/unity_sps_ogc_processes_api/models/crs.py @@ -56,9 +56,13 @@ class Crs(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app/src/unity_sps_ogc_processes_api/models/crs_one_of.py b/app/src/unity_sps_ogc_processes_api/models/crs_one_of.py index 858ae6c..e059e18 100644 --- a/app/src/unity_sps_ogc_processes_api/models/crs_one_of.py +++ b/app/src/unity_sps_ogc_processes_api/models/crs_one_of.py @@ -46,8 +46,12 @@ class CrsOneOf(BaseModel): oneof_schema_2_validator: Optional[CrsOneOfOneOf1] = None # data type: CrsOneOfOneOf2 oneof_schema_3_validator: Optional[CrsOneOfOneOf2] = None - actual_instance: Optional[Union[CrsOneOfOneOf, CrsOneOfOneOf1, CrsOneOfOneOf2]] = None - one_of_schemas: List[str] = Literal["CrsOneOfOneOf", "CrsOneOfOneOf1", "CrsOneOfOneOf2"] + actual_instance: Optional[Union[CrsOneOfOneOf, CrsOneOfOneOf1, CrsOneOfOneOf2]] = ( + None + ) + one_of_schemas: List[str] = Literal[ + "CrsOneOfOneOf", "CrsOneOfOneOf1", "CrsOneOfOneOf2" + ] model_config = { "validate_assignment": True, @@ -57,9 +61,13 @@ class CrsOneOf(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -71,17 +79,23 @@ def actual_instance_must_validate_oneof(cls, v): match = 0 # validate data type: CrsOneOfOneOf if not isinstance(v, CrsOneOfOneOf): - error_messages.append(f"Error! Input type `{type(v)}` is not `CrsOneOfOneOf`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `CrsOneOfOneOf`" + ) else: match += 1 # validate data type: CrsOneOfOneOf1 if not isinstance(v, CrsOneOfOneOf1): - error_messages.append(f"Error! Input type `{type(v)}` is not `CrsOneOfOneOf1`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `CrsOneOfOneOf1`" + ) else: match += 1 # validate data type: CrsOneOfOneOf2 if not isinstance(v, CrsOneOfOneOf2): - error_messages.append(f"Error! Input type `{type(v)}` is not `CrsOneOfOneOf2`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `CrsOneOfOneOf2`" + ) else: match += 1 if match > 1: diff --git a/app/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py b/app/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py index 7adcd6f..bb018ae 100644 --- a/app/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py +++ b/app/src/unity_sps_ogc_processes_api/models/crs_one_of_one_of.py @@ -33,7 +33,9 @@ class CrsOneOfOneOf(BaseModel): CrsOneOfOneOf """ # noqa: E501 - uri: StrictStr = Field(description="Reference to one coordinate reference system (CRS)") + uri: StrictStr = Field( + description="Reference to one coordinate reference system (CRS)" + ) __properties: ClassVar[List[str]] = ["uri"] model_config = { diff --git a/app/src/unity_sps_ogc_processes_api/models/data_type.py b/app/src/unity_sps_ogc_processes_api/models/data_type.py index 55755a0..c530760 100644 --- a/app/src/unity_sps_ogc_processes_api/models/data_type.py +++ b/app/src/unity_sps_ogc_processes_api/models/data_type.py @@ -51,9 +51,13 @@ class DataType(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app/src/unity_sps_ogc_processes_api/models/execute.py b/app/src/unity_sps_ogc_processes_api/models/execute.py index 729152f..ce1da08 100644 --- a/app/src/unity_sps_ogc_processes_api/models/execute.py +++ b/app/src/unity_sps_ogc_processes_api/models/execute.py @@ -108,17 +108,25 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { "inputs": ( - dict((_k, Input.from_dict(_v)) for _k, _v in obj.get("inputs").items()) + dict( + (_k, Input.from_dict(_v)) + for _k, _v in obj.get("inputs").items() + ) if obj.get("inputs") is not None else None ), "outputs": ( - dict((_k, Output.from_dict(_v)) for _k, _v in obj.get("outputs").items()) + dict( + (_k, Output.from_dict(_v)) + for _k, _v in obj.get("outputs").items() + ) if obj.get("outputs") is not None else None ), "subscriber": ( - Subscriber.from_dict(obj.get("subscriber")) if obj.get("subscriber") is not None else None + Subscriber.from_dict(obj.get("subscriber")) + if obj.get("subscriber") is not None + else None ), } ) diff --git a/app/src/unity_sps_ogc_processes_api/models/execute200_response.py b/app/src/unity_sps_ogc_processes_api/models/execute200_response.py index fa2c7e6..77f0081 100644 --- a/app/src/unity_sps_ogc_processes_api/models/execute200_response.py +++ b/app/src/unity_sps_ogc_processes_api/models/execute200_response.py @@ -50,7 +50,9 @@ def validate_root(cls, v): if isinstance(v, dict): for key, value in v.items(): if not isinstance(key, str): - raise ValueError(f"Dictionary keys must be strings, got {type(key)}") + raise ValueError( + f"Dictionary keys must be strings, got {type(key)}" + ) if not isinstance( value, (Bbox, list, bool, float, int, str, Link, QualifiedInputValue), diff --git a/app/src/unity_sps_ogc_processes_api/models/execute200_response1.py b/app/src/unity_sps_ogc_processes_api/models/execute200_response1.py index e7e2478..7392912 100644 --- a/app/src/unity_sps_ogc_processes_api/models/execute200_response1.py +++ b/app/src/unity_sps_ogc_processes_api/models/execute200_response1.py @@ -45,7 +45,9 @@ class Execute200Response1(BaseModel): type: StrictStr features: List[GeoJSONFeature] - bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None __properties: ClassVar[List[str]] = ["type", "features", "bbox"] @field_validator("type") diff --git a/app/src/unity_sps_ogc_processes_api/models/execute_workflows.py b/app/src/unity_sps_ogc_processes_api/models/execute_workflows.py index 087955f..fbe895c 100644 --- a/app/src/unity_sps_ogc_processes_api/models/execute_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/execute_workflows.py @@ -137,17 +137,25 @@ def from_dict(cls, obj: Dict) -> Self: "sortBy": obj.get("sortBy"), "process": obj.get("process"), "inputs": ( - dict((_k, InputWorkflows.from_dict(_v)) for _k, _v in obj.get("inputs").items()) + dict( + (_k, InputWorkflows.from_dict(_v)) + for _k, _v in obj.get("inputs").items() + ) if obj.get("inputs") is not None else None ), "outputs": ( - dict((_k, OutputWorkflows.from_dict(_v)) for _k, _v in obj.get("outputs").items()) + dict( + (_k, OutputWorkflows.from_dict(_v)) + for _k, _v in obj.get("outputs").items() + ) if obj.get("outputs") is not None else None ), "subscriber": ( - Subscriber.from_dict(obj.get("subscriber")) if obj.get("subscriber") is not None else None + Subscriber.from_dict(obj.get("subscriber")) + if obj.get("subscriber") is not None + else None ), } ) diff --git a/app/src/unity_sps_ogc_processes_api/models/execute_workflows1.py b/app/src/unity_sps_ogc_processes_api/models/execute_workflows1.py index 2f66be7..0336057 100644 --- a/app/src/unity_sps_ogc_processes_api/models/execute_workflows1.py +++ b/app/src/unity_sps_ogc_processes_api/models/execute_workflows1.py @@ -137,17 +137,25 @@ def from_dict(cls, obj: Dict) -> Self: "sortBy": obj.get("sortBy"), "process": obj.get("process"), "inputs": ( - dict((_k, InputWorkflows1.from_dict(_v)) for _k, _v in obj.get("inputs").items()) + dict( + (_k, InputWorkflows1.from_dict(_v)) + for _k, _v in obj.get("inputs").items() + ) if obj.get("inputs") is not None else None ), "outputs": ( - dict((_k, OutputWorkflows1.from_dict(_v)) for _k, _v in obj.get("outputs").items()) + dict( + (_k, OutputWorkflows1.from_dict(_v)) + for _k, _v in obj.get("outputs").items() + ) if obj.get("outputs") is not None else None ), "subscriber": ( - Subscriber.from_dict(obj.get("subscriber")) if obj.get("subscriber") is not None else None + Subscriber.from_dict(obj.get("subscriber")) + if obj.get("subscriber") is not None + else None ), } ) diff --git a/app/src/unity_sps_ogc_processes_api/models/execution_unit.py b/app/src/unity_sps_ogc_processes_api/models/execution_unit.py index 7ea86a4..db04341 100644 --- a/app/src/unity_sps_ogc_processes_api/models/execution_unit.py +++ b/app/src/unity_sps_ogc_processes_api/models/execution_unit.py @@ -36,7 +36,9 @@ class ExecutionUnit(BaseModel): """ # noqa: E501 type: StrictStr = Field(description="Type of execution unit.") - image: StrictStr = Field(description="Container image reference for the execution unit.") + image: StrictStr = Field( + description="Container image reference for the execution unit." + ) deployment: Optional[StrictStr] = Field( default=None, description="Deployment information for the execution unit." ) @@ -58,7 +60,9 @@ def deployment_validate_enum(cls, value): return value if value not in ("local", "remote", "hpc", "cloud"): - raise ValueError("must be one of enum values ('local', 'remote', 'hpc', 'cloud')") + raise ValueError( + "must be one of enum values ('local', 'remote', 'hpc', 'cloud')" + ) return value model_config = { diff --git a/app/src/unity_sps_ogc_processes_api/models/extent.py b/app/src/unity_sps_ogc_processes_api/models/extent.py index 12104a7..f5f48ee 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent.py @@ -95,10 +95,14 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { "spatial": ( - ExtentSpatial.from_dict(obj.get("spatial")) if obj.get("spatial") is not None else None + ExtentSpatial.from_dict(obj.get("spatial")) + if obj.get("spatial") is not None + else None ), "temporal": ( - ExtentTemporal.from_dict(obj.get("temporal")) if obj.get("temporal") is not None else None + ExtentTemporal.from_dict(obj.get("temporal")) + if obj.get("temporal") is not None + else None ), } ) diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_spatial.py b/app/src/unity_sps_ogc_processes_api/models/extent_spatial.py index 90f0ac4..15666e6 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_spatial.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_spatial.py @@ -45,7 +45,9 @@ class ExtentSpatial(BaseModel): The spatial extent of the data in the collection. """ # noqa: E501 - bbox: Optional[Annotated[List[List[Union[StrictFloat, StrictInt]]], Field(min_length=1)]] = Field( + bbox: Optional[ + Annotated[List[List[Union[StrictFloat, StrictInt]]], Field(min_length=1)] + ] = Field( default=None, description="One or more bounding boxes that describe the spatial extent of the dataset. In the Core only a single bounding box is supported. Extensions may support additional areas. The first bounding box describes the overall spatial extent of the data. All subsequent bounding boxes describe more precise bounding boxes, e.g., to identify clusters of data. Clients only interested in the overall spatial extent will only need to access the first item in each array.", ) @@ -53,7 +55,9 @@ class ExtentSpatial(BaseModel): default="1.3/CRS84", description="Coordinate reference system of the coordinates in the spatial extent (property `bbox`). The default reference system is WGS 84 longitude/latitude. In the Core the only other supported coordinate reference system is WGS 84 longitude/latitude/ellipsoidal height for coordinates with height. Extensions may support additional coordinate reference systems and add additional enum values.", ) - grid: Optional[Annotated[List[ExtentSpatialGridInner], Field(min_length=2, max_length=3)]] = Field( + grid: Optional[ + Annotated[List[ExtentSpatialGridInner], Field(min_length=2, max_length=3)] + ] = Field( default=None, description="Provides information about the limited availability of data within the collection organized as a grid (regular or irregular) along each spatial dimension.", ) @@ -132,7 +136,10 @@ def from_dict(cls, obj: Dict) -> Self: "bbox": obj.get("bbox"), "crs": obj.get("crs") if obj.get("crs") is not None else "1.3/CRS84", "grid": ( - [ExtentSpatialGridInner.from_dict(_item) for _item in obj.get("grid")] + [ + ExtentSpatialGridInner.from_dict(_item) + for _item in obj.get("grid") + ] if obj.get("grid") is not None else None ), diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py index a35bd21..d943b10 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner.py @@ -41,11 +41,11 @@ class ExtentSpatialGridInner(BaseModel): ExtentSpatialGridInner """ # noqa: E501 - coordinates: Optional[Annotated[List[ExtentSpatialGridInnerCoordinatesInner], Field(min_length=1)]] = ( - Field( - default=None, - description="List of coordinates along the dimension for which data organized as an irregular grid in the collection is available (e.g., 2, 10, 80, 100).", - ) + coordinates: Optional[ + Annotated[List[ExtentSpatialGridInnerCoordinatesInner], Field(min_length=1)] + ] = Field( + default=None, + description="List of coordinates along the dimension for which data organized as an irregular grid in the collection is available (e.g., 2, 10, 80, 100).", ) cells_count: Optional[StrictInt] = Field( default=None, diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py index 2c1def8..40e0685 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_coordinates_inner.py @@ -58,9 +58,13 @@ class ExtentSpatialGridInnerCoordinatesInner(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py index 8286284..4f87b6c 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_spatial_grid_inner_resolution.py @@ -58,9 +58,13 @@ class ExtentSpatialGridInnerResolution(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_temporal.py b/app/src/unity_sps_ogc_processes_api/models/extent_temporal.py index 5d38380..a30bbec 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_temporal.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_temporal.py @@ -39,7 +39,9 @@ class ExtentTemporal(BaseModel): interval: Optional[ Annotated[ - List[Annotated[List[Optional[datetime]], Field(min_length=2, max_length=2)]], + List[ + Annotated[List[Optional[datetime]], Field(min_length=2, max_length=2)] + ], Field(min_length=1), ] ] = Field( @@ -123,7 +125,9 @@ def from_dict(cls, obj: Dict) -> Self: else "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian" ), "grid": ( - ExtentTemporalGrid.from_dict(obj.get("grid")) if obj.get("grid") is not None else None + ExtentTemporalGrid.from_dict(obj.get("grid")) + if obj.get("grid") is not None + else None ), } ) diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py b/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py index 83b0821..8b24a5b 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py @@ -38,9 +38,11 @@ class ExtentTemporalGrid(BaseModel): Provides information about the limited availability of data within the collection organized as a grid (regular or irregular) along the temporal dimension. """ # noqa: E501 - coordinates: Optional[Annotated[List[Optional[StrictStr]], Field(min_length=1)]] = Field( - default=None, - description='List of coordinates along the temporal dimension for which data organized as an irregular grid in the collection is available (e.g., "2017-11-14T09:00Z","2017-11-14T12:00Z","2017-11-14T15:00Z","2017-11-14T18:00Z","2017-11-14T21:00Z").', + coordinates: Optional[Annotated[List[Optional[StrictStr]], Field(min_length=1)]] = ( + Field( + default=None, + description='List of coordinates along the temporal dimension for which data organized as an irregular grid in the collection is available (e.g., "2017-11-14T09:00Z","2017-11-14T12:00Z","2017-11-14T15:00Z","2017-11-14T18:00Z","2017-11-14T21:00Z").', + ) ) cells_count: Optional[StrictInt] = Field( default=None, diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py b/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py index 2386d64..6164e15 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid_resolution.py @@ -58,9 +58,13 @@ class ExtentTemporalGridResolution(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app/src/unity_sps_ogc_processes_api/models/extent_uad.py b/app/src/unity_sps_ogc_processes_api/models/extent_uad.py index 6fad392..6a09966 100644 --- a/app/src/unity_sps_ogc_processes_api/models/extent_uad.py +++ b/app/src/unity_sps_ogc_processes_api/models/extent_uad.py @@ -95,10 +95,14 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { "spatial": ( - ExtentSpatial.from_dict(obj.get("spatial")) if obj.get("spatial") is not None else None + ExtentSpatial.from_dict(obj.get("spatial")) + if obj.get("spatial") is not None + else None ), "temporal": ( - ExtentTemporal.from_dict(obj.get("temporal")) if obj.get("temporal") is not None else None + ExtentTemporal.from_dict(obj.get("temporal")) + if obj.get("temporal") is not None + else None ), } ) diff --git a/app/src/unity_sps_ogc_processes_api/models/feature_collection.py b/app/src/unity_sps_ogc_processes_api/models/feature_collection.py index e07b601..d9d2f08 100644 --- a/app/src/unity_sps_ogc_processes_api/models/feature_collection.py +++ b/app/src/unity_sps_ogc_processes_api/models/feature_collection.py @@ -45,7 +45,9 @@ class FeatureCollection(BaseModel): type: StrictStr features: List[GeoJSONFeature] - bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None __properties: ClassVar[List[str]] = ["type", "features", "bbox"] @field_validator("type") diff --git a/app/src/unity_sps_ogc_processes_api/models/format.py b/app/src/unity_sps_ogc_processes_api/models/format.py index c795faf..8079d8d 100644 --- a/app/src/unity_sps_ogc_processes_api/models/format.py +++ b/app/src/unity_sps_ogc_processes_api/models/format.py @@ -94,7 +94,9 @@ def from_dict(cls, obj: Dict) -> Self: "mediaType": obj.get("mediaType"), "encoding": obj.get("encoding"), "schema": ( - FormatSchema.from_dict(obj.get("schema")) if obj.get("schema") is not None else None + FormatSchema.from_dict(obj.get("schema")) + if obj.get("schema") is not None + else None ), } ) diff --git a/app/src/unity_sps_ogc_processes_api/models/format_schema.py b/app/src/unity_sps_ogc_processes_api/models/format_schema.py index adf0598..f0f0067 100644 --- a/app/src/unity_sps_ogc_processes_api/models/format_schema.py +++ b/app/src/unity_sps_ogc_processes_api/models/format_schema.py @@ -51,9 +51,13 @@ class FormatSchema(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_feature.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature.py index 544ff11..ce8da2e 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_feature.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature.py @@ -50,7 +50,9 @@ class GeoJSONFeature(BaseModel): id: Optional[GeoJSONFeatureId] = None properties: Optional[Dict[str, Any]] geometry: GeoJSONFeatureGeometry - bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None __properties: ClassVar[List[str]] = ["type", "id", "properties", "geometry", "bbox"] @field_validator("type") @@ -120,7 +122,11 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { "type": obj.get("type"), - "id": (GeoJSONFeatureId.from_dict(obj.get("id")) if obj.get("id") is not None else None), + "id": ( + GeoJSONFeatureId.from_dict(obj.get("id")) + if obj.get("id") is not None + else None + ), "properties": obj.get("properties"), "geometry": ( GeoJSONFeatureGeometry.from_dict(obj.get("geometry")) diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py index 0a0799e..557cefd 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_geometry.py @@ -93,9 +93,13 @@ class GeoJSONFeatureGeometry(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -107,32 +111,44 @@ def actual_instance_must_validate_oneof(cls, v): match = 0 # validate data type: GeoJSONPoint if not isinstance(v, GeoJSONPoint): - error_messages.append(f"Error! Input type `{type(v)}` is not `GeoJSONPoint`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `GeoJSONPoint`" + ) else: match += 1 # validate data type: GeoJSONLineString if not isinstance(v, GeoJSONLineString): - error_messages.append(f"Error! Input type `{type(v)}` is not `GeoJSONLineString`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `GeoJSONLineString`" + ) else: match += 1 # validate data type: GeoJSONPolygon if not isinstance(v, GeoJSONPolygon): - error_messages.append(f"Error! Input type `{type(v)}` is not `GeoJSONPolygon`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `GeoJSONPolygon`" + ) else: match += 1 # validate data type: GeoJSONMultiPoint if not isinstance(v, GeoJSONMultiPoint): - error_messages.append(f"Error! Input type `{type(v)}` is not `GeoJSONMultiPoint`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `GeoJSONMultiPoint`" + ) else: match += 1 # validate data type: GeoJSONMultiLineString if not isinstance(v, GeoJSONMultiLineString): - error_messages.append(f"Error! Input type `{type(v)}` is not `GeoJSONMultiLineString`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `GeoJSONMultiLineString`" + ) else: match += 1 # validate data type: GeoJSONMultiPolygon if not isinstance(v, GeoJSONMultiPolygon): - error_messages.append(f"Error! Input type `{type(v)}` is not `GeoJSONMultiPolygon`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `GeoJSONMultiPolygon`" + ) else: match += 1 if match > 1: diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py index 7407599..7062157 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature_id.py @@ -58,9 +58,13 @@ class GeoJSONFeatureId(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py index 02e4d4d..5e38591 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_line_string.py @@ -46,7 +46,9 @@ class GeoJSONLineString(BaseModel): List[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)]], Field(min_length=2), ] - bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] @field_validator("type") diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py index 726ca9f..4e13cb8 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_line_string.py @@ -48,7 +48,9 @@ class GeoJSONMultiLineString(BaseModel): Field(min_length=2), ] ] - bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] @field_validator("type") diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py index da59f08..8d64573 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_point.py @@ -42,8 +42,12 @@ class GeoJSONMultiPoint(BaseModel): """ # noqa: E501 type: StrictStr - coordinates: List[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)]] - bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None + coordinates: List[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)] + ] + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] @field_validator("type") diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py index 1bc084c..56775ac 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_multi_polygon.py @@ -45,12 +45,16 @@ class GeoJSONMultiPolygon(BaseModel): coordinates: List[ List[ Annotated[ - List[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)]], + List[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)] + ], Field(min_length=4), ] ] ] - bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] @field_validator("type") diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_point.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_point.py index 6683680..c8eb97f 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_point.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_point.py @@ -43,7 +43,9 @@ class GeoJSONPoint(BaseModel): type: StrictStr coordinates: Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=2)] - bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] @field_validator("type") diff --git a/app/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py index d4d32ac..c535824 100644 --- a/app/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py +++ b/app/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py @@ -48,7 +48,9 @@ class GeoJSONPolygon(BaseModel): Field(min_length=4), ] ] - bbox: Optional[Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)]] = None + bbox: Optional[ + Annotated[List[Union[StrictFloat, StrictInt]], Field(min_length=4)] + ] = None __properties: ClassVar[List[str]] = ["type", "coordinates", "bbox"] @field_validator("type") diff --git a/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py index 87f931f..2bba538 100644 --- a/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py +++ b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py @@ -40,7 +40,9 @@ def validate_type(cls, value): if "bbox" in value: return Bbox(**value) return value # Handle other dict cases - elif isinstance(value, (Bbox, Link, QualifiedInputValue, bool, int, float, str)): + elif isinstance( + value, (Bbox, Link, QualifiedInputValue, bool, int, float, str) + ): return value elif isinstance(value, list): return value diff --git a/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py index 6eb03a4..0bf5526 100644 --- a/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py +++ b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data1.py @@ -54,8 +54,12 @@ class InlineOrRefData1(BaseModel): oneof_schema_2_validator: Optional[QualifiedInputValue1] = None # data type: Link oneof_schema_3_validator: Optional[Link] = None - actual_instance: Optional[Union[InputValueNoObject1, Link, QualifiedInputValue1]] = None - one_of_schemas: List[str] = Literal["InputValueNoObject1", "Link", "QualifiedInputValue1"] + actual_instance: Optional[ + Union[InputValueNoObject1, Link, QualifiedInputValue1] + ] = None + one_of_schemas: List[str] = Literal[ + "InputValueNoObject1", "Link", "QualifiedInputValue1" + ] model_config = { "validate_assignment": True, @@ -65,9 +69,13 @@ class InlineOrRefData1(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -79,12 +87,16 @@ def actual_instance_must_validate_oneof(cls, v): match = 0 # validate data type: InputValueNoObject1 if not isinstance(v, InputValueNoObject1): - error_messages.append(f"Error! Input type `{type(v)}` is not `InputValueNoObject1`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputValueNoObject1`" + ) else: match += 1 # validate data type: QualifiedInputValue1 if not isinstance(v, QualifiedInputValue1): - error_messages.append(f"Error! Input type `{type(v)}` is not `QualifiedInputValue1`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `QualifiedInputValue1`" + ) else: match += 1 # validate data type: Link diff --git a/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py index d44f580..7714621 100644 --- a/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data_workflows.py @@ -48,8 +48,12 @@ class InlineOrRefDataWorkflows(BaseModel): oneof_schema_2_validator: Optional[QualifiedInputValueWorkflows] = None # data type: Link oneof_schema_3_validator: Optional[Link] = None - actual_instance: Optional[Union[InputValueNoObjectWorkflows, Link, QualifiedInputValueWorkflows]] = None - one_of_schemas: List[str] = Literal["InputValueNoObjectWorkflows", "Link", "QualifiedInputValueWorkflows"] + actual_instance: Optional[ + Union[InputValueNoObjectWorkflows, Link, QualifiedInputValueWorkflows] + ] = None + one_of_schemas: List[str] = Literal[ + "InputValueNoObjectWorkflows", "Link", "QualifiedInputValueWorkflows" + ] model_config = { "validate_assignment": True, @@ -59,9 +63,13 @@ class InlineOrRefDataWorkflows(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -73,12 +81,16 @@ def actual_instance_must_validate_oneof(cls, v): match = 0 # validate data type: InputValueNoObjectWorkflows if not isinstance(v, InputValueNoObjectWorkflows): - error_messages.append(f"Error! Input type `{type(v)}` is not `InputValueNoObjectWorkflows`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputValueNoObjectWorkflows`" + ) else: match += 1 # validate data type: QualifiedInputValueWorkflows if not isinstance(v, QualifiedInputValueWorkflows): - error_messages.append(f"Error! Input type `{type(v)}` is not `QualifiedInputValueWorkflows`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `QualifiedInputValueWorkflows`" + ) else: match += 1 # validate data type: Link diff --git a/app/src/unity_sps_ogc_processes_api/models/input.py b/app/src/unity_sps_ogc_processes_api/models/input.py index 5a5ee1b..f0c3271 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input.py +++ b/app/src/unity_sps_ogc_processes_api/models/input.py @@ -23,7 +23,9 @@ class Input(RootModel): str, Link, QualifiedInputValue1, - List[Union[Bbox1, List[Any], bool, float, int, str, Link, QualifiedInputValue1]], + List[ + Union[Bbox1, List[Any], bool, float, int, str, Link, QualifiedInputValue1] + ], ] @model_validator(mode="before") @@ -38,7 +40,9 @@ def validate_type(cls, value): return Link(**value) elif isinstance(value, list): return [cls.validate_type(item) for item in value] - elif isinstance(value, (bool, int, float, str, Bbox1, Link, QualifiedInputValue1)): + elif isinstance( + value, (bool, int, float, str, Bbox1, Link, QualifiedInputValue1) + ): return value elif isinstance(value, List): return value diff --git a/app/src/unity_sps_ogc_processes_api/models/input_description.py b/app/src/unity_sps_ogc_processes_api/models/input_description.py index 69e9b1b..3cda30e 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_description.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_description.py @@ -66,7 +66,9 @@ def value_passing_validate_enum(cls, value): for i in value: if i not in ("byValue", "byReference"): - raise ValueError("each list item must be one of ('byValue', 'byReference')") + raise ValueError( + "each list item must be one of ('byValue', 'byReference')" + ) return value model_config = { @@ -139,9 +141,13 @@ def from_dict(cls, obj: Dict) -> Self: else None ), "schema": ( - ModelSchema.from_dict(obj.get("schema")) if obj.get("schema") is not None else None + ModelSchema.from_dict(obj.get("schema")) + if obj.get("schema") is not None + else None + ), + "minOccurs": ( + obj.get("minOccurs") if obj.get("minOccurs") is not None else 1 ), - "minOccurs": (obj.get("minOccurs") if obj.get("minOccurs") is not None else 1), "maxOccurs": ( InputDescriptionAllOfMaxOccurs.from_dict(obj.get("maxOccurs")) if obj.get("maxOccurs") is not None diff --git a/app/src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py b/app/src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py index c58ca3b..20d2005 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_description_all_of_max_occurs.py @@ -51,9 +51,13 @@ class InputDescriptionAllOfMaxOccurs(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app/src/unity_sps_ogc_processes_api/models/input_process.py b/app/src/unity_sps_ogc_processes_api/models/input_process.py index d641326..e80c881 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_process.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_process.py @@ -128,17 +128,25 @@ def from_dict(cls, obj: Dict) -> Self: { "process": obj.get("process"), "inputs": ( - dict((_k, InputWorkflows1.from_dict(_v)) for _k, _v in obj.get("inputs").items()) + dict( + (_k, InputWorkflows1.from_dict(_v)) + for _k, _v in obj.get("inputs").items() + ) if obj.get("inputs") is not None else None ), "outputs": ( - dict((_k, OutputWorkflows1.from_dict(_v)) for _k, _v in obj.get("outputs").items()) + dict( + (_k, OutputWorkflows1.from_dict(_v)) + for _k, _v in obj.get("outputs").items() + ) if obj.get("outputs") is not None else None ), "subscriber": ( - Subscriber.from_dict(obj.get("subscriber")) if obj.get("subscriber") is not None else None + Subscriber.from_dict(obj.get("subscriber")) + if obj.get("subscriber") is not None + else None ), "filter": obj.get("filter"), "properties": ( diff --git a/app/src/unity_sps_ogc_processes_api/models/input_value.py b/app/src/unity_sps_ogc_processes_api/models/input_value.py index c8d8a66..aec7958 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_value.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value.py @@ -56,9 +56,13 @@ class InputValue(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -69,7 +73,9 @@ def actual_instance_must_validate_anyof(cls, v): error_messages = [] # validate data type: InputValueNoObject if not isinstance(v, InputValueNoObject): - error_messages.append(f"Error! Input type `{type(v)}` is not `InputValueNoObject`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputValueNoObject`" + ) else: return v diff --git a/app/src/unity_sps_ogc_processes_api/models/input_value1.py b/app/src/unity_sps_ogc_processes_api/models/input_value1.py index bccdf85..e51aafd 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_value1.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value1.py @@ -58,9 +58,13 @@ class InputValue1(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -71,7 +75,9 @@ def actual_instance_must_validate_anyof(cls, v): error_messages = [] # validate data type: InputValueNoObject1 if not isinstance(v, InputValueNoObject1): - error_messages.append(f"Error! Input type `{type(v)}` is not `InputValueNoObject1`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputValueNoObject1`" + ) else: return v diff --git a/app/src/unity_sps_ogc_processes_api/models/input_value_no_object.py b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object.py index e327c7d..fc969fd 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_value_no_object.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object.py @@ -69,7 +69,9 @@ class InputValueNoObject(BaseModel): # data type: Bbox oneof_schema_7_validator: Optional[Bbox] = None actual_instance: Optional[Union[Bbox, List[object], bool, float, int, str]] = None - one_of_schemas: List[str] = Literal["Bbox", "List[object]", "bool", "float", "int", "str"] + one_of_schemas: List[str] = Literal[ + "Bbox", "List[object]", "bool", "float", "int", "str" + ] model_config = { "validate_assignment": True, @@ -79,9 +81,13 @@ class InputValueNoObject(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py index 575f620..9dd2681 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object1.py @@ -69,7 +69,9 @@ class InputValueNoObject1(BaseModel): # data type: Bbox1 oneof_schema_7_validator: Optional[Bbox1] = None actual_instance: Optional[Union[Bbox1, List[object], bool, float, int, str]] = None - one_of_schemas: List[str] = Literal["Bbox1", "List[object]", "bool", "float", "int", "str"] + one_of_schemas: List[str] = Literal[ + "Bbox1", "List[object]", "bool", "float", "int", "str" + ] model_config = { "validate_assignment": True, @@ -79,9 +81,13 @@ class InputValueNoObject1(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py index 468c719..fc49e7e 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value_no_object_workflows.py @@ -112,9 +112,13 @@ class InputValueNoObjectWorkflows(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -167,17 +171,23 @@ def actual_instance_must_validate_oneof(cls, v): match += 1 # validate data type: InputCollection if not isinstance(v, InputCollection): - error_messages.append(f"Error! Input type `{type(v)}` is not `InputCollection`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputCollection`" + ) else: match += 1 # validate data type: InputProcess if not isinstance(v, InputProcess): - error_messages.append(f"Error! Input type `{type(v)}` is not `InputProcess`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputProcess`" + ) else: match += 1 # validate data type: InputParameterized if not isinstance(v, InputParameterized): - error_messages.append(f"Error! Input type `{type(v)}` is not `InputParameterized`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputParameterized`" + ) else: match += 1 if match > 1: diff --git a/app/src/unity_sps_ogc_processes_api/models/input_value_workflows.py b/app/src/unity_sps_ogc_processes_api/models/input_value_workflows.py index 4b83a74..6d18d3a 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_value_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_value_workflows.py @@ -51,9 +51,13 @@ class InputValueWorkflows(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -65,7 +69,9 @@ def actual_instance_must_validate_oneof(cls, v): match = 0 # validate data type: InputValueNoObjectWorkflows if not isinstance(v, InputValueNoObjectWorkflows): - error_messages.append(f"Error! Input type `{type(v)}` is not `InputValueNoObjectWorkflows`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `InputValueNoObjectWorkflows`" + ) else: match += 1 # validate data type: object diff --git a/app/src/unity_sps_ogc_processes_api/models/input_workflows1.py b/app/src/unity_sps_ogc_processes_api/models/input_workflows1.py index d5ebcf5..6dd9a1e 100644 --- a/app/src/unity_sps_ogc_processes_api/models/input_workflows1.py +++ b/app/src/unity_sps_ogc_processes_api/models/input_workflows1.py @@ -43,8 +43,12 @@ class InputWorkflows1(BaseModel): oneof_schema_1_validator: Optional[InlineOrRefDataWorkflows] = None # data type: List[InlineOrRefDataWorkflows] oneof_schema_2_validator: Optional[List[InlineOrRefDataWorkflows]] = None - actual_instance: Optional[Union[InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]]] = None - one_of_schemas: List[str] = Literal["InlineOrRefDataWorkflows", "List[InlineOrRefDataWorkflows]"] + actual_instance: Optional[ + Union[InlineOrRefDataWorkflows, List[InlineOrRefDataWorkflows]] + ] = None + one_of_schemas: List[str] = Literal[ + "InlineOrRefDataWorkflows", "List[InlineOrRefDataWorkflows]" + ] model_config = { "validate_assignment": True, @@ -54,9 +58,13 @@ class InputWorkflows1(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -68,7 +76,9 @@ def actual_instance_must_validate_oneof(cls, v): match = 0 # validate data type: InlineOrRefDataWorkflows if not isinstance(v, InlineOrRefDataWorkflows): - error_messages.append(f"Error! Input type `{type(v)}` is not `InlineOrRefDataWorkflows`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `InlineOrRefDataWorkflows`" + ) else: match += 1 # validate data type: List[InlineOrRefDataWorkflows] diff --git a/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py b/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py index df985c6..cc3eeb3 100644 --- a/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py +++ b/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py @@ -98,7 +98,9 @@ def from_dict(cls, obj: Dict) -> Self: "title": obj.get("title"), "lang": obj.get("lang"), "value": ( - MetadataOneOf1Value.from_dict(obj.get("value")) if obj.get("value") is not None else None + MetadataOneOf1Value.from_dict(obj.get("value")) + if obj.get("value") is not None + else None ), } ) diff --git a/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py b/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py index 20ee6df..2930109 100644 --- a/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py +++ b/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1_value.py @@ -51,9 +51,13 @@ class MetadataOneOf1Value(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py index 44859f2..eea9993 100644 --- a/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py +++ b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py @@ -57,9 +57,13 @@ class OgcapppkgArrayInner(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) @@ -71,7 +75,9 @@ def actual_instance_must_validate_oneof(cls, v): match = 0 # validate data type: ExecutionUnit if not isinstance(v, ExecutionUnit): - error_messages.append(f"Error! Input type `{type(v)}` is not `ExecutionUnit`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `ExecutionUnit`" + ) else: match += 1 # validate data type: Link @@ -81,7 +87,9 @@ def actual_instance_must_validate_oneof(cls, v): match += 1 # validate data type: QualifiedInputValue if not isinstance(v, QualifiedInputValue): - error_messages.append(f"Error! Input type `{type(v)}` is not `QualifiedInputValue`") + error_messages.append( + f"Error! Input type `{type(v)}` is not `QualifiedInputValue`" + ) else: match += 1 if match > 1: diff --git a/app/src/unity_sps_ogc_processes_api/models/output.py b/app/src/unity_sps_ogc_processes_api/models/output.py index a93851a..bfc5305 100644 --- a/app/src/unity_sps_ogc_processes_api/models/output.py +++ b/app/src/unity_sps_ogc_processes_api/models/output.py @@ -88,6 +88,12 @@ def from_dict(cls, obj: Dict) -> Self: return cls.model_validate(obj) _obj = cls.model_validate( - {"format": (Format.from_dict(obj.get("format")) if obj.get("format") is not None else None)} + { + "format": ( + Format.from_dict(obj.get("format")) + if obj.get("format") is not None + else None + ) + } ) return _obj diff --git a/app/src/unity_sps_ogc_processes_api/models/output_description.py b/app/src/unity_sps_ogc_processes_api/models/output_description.py index 4b81393..7b52c03 100644 --- a/app/src/unity_sps_ogc_processes_api/models/output_description.py +++ b/app/src/unity_sps_ogc_processes_api/models/output_description.py @@ -116,7 +116,9 @@ def from_dict(cls, obj: Dict) -> Self: else None ), "schema": ( - ModelSchema.from_dict(obj.get("schema")) if obj.get("schema") is not None else None + ModelSchema.from_dict(obj.get("schema")) + if obj.get("schema") is not None + else None ), } ) diff --git a/app/src/unity_sps_ogc_processes_api/models/output_workflows.py b/app/src/unity_sps_ogc_processes_api/models/output_workflows.py index 9db9b75..7431654 100644 --- a/app/src/unity_sps_ogc_processes_api/models/output_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/output_workflows.py @@ -90,7 +90,11 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { - "format": (Format.from_dict(obj.get("format")) if obj.get("format") is not None else None), + "format": ( + Format.from_dict(obj.get("format")) + if obj.get("format") is not None + else None + ), "$output": obj.get("$output"), } ) diff --git a/app/src/unity_sps_ogc_processes_api/models/output_workflows1.py b/app/src/unity_sps_ogc_processes_api/models/output_workflows1.py index 6720f17..831e686 100644 --- a/app/src/unity_sps_ogc_processes_api/models/output_workflows1.py +++ b/app/src/unity_sps_ogc_processes_api/models/output_workflows1.py @@ -90,7 +90,11 @@ def from_dict(cls, obj: Dict) -> Self: _obj = cls.model_validate( { - "format": (Format.from_dict(obj.get("format")) if obj.get("format") is not None else None), + "format": ( + Format.from_dict(obj.get("format")) + if obj.get("format") is not None + else None + ), "$output": obj.get("$output"), } ) diff --git a/app/src/unity_sps_ogc_processes_api/models/process.py b/app/src/unity_sps_ogc_processes_api/models/process.py index 0095243..af787f9 100644 --- a/app/src/unity_sps_ogc_processes_api/models/process.py +++ b/app/src/unity_sps_ogc_processes_api/models/process.py @@ -45,7 +45,9 @@ class Process(BaseModel): metadata: Optional[List[Metadata]] = None id: StrictStr version: StrictStr - job_control_options: Optional[List[JobControlOptions]] = Field(default=None, alias="jobControlOptions") + job_control_options: Optional[List[JobControlOptions]] = Field( + default=None, alias="jobControlOptions" + ) links: Optional[List[Link]] = None inputs: Optional[Dict[str, InputDescription]] = None outputs: Optional[Dict[str, OutputDescription]] = None @@ -155,12 +157,18 @@ def from_dict(cls, obj: Dict) -> Self: else None ), "inputs": ( - dict((_k, InputDescription.from_dict(_v)) for _k, _v in obj.get("inputs").items()) + dict( + (_k, InputDescription.from_dict(_v)) + for _k, _v in obj.get("inputs").items() + ) if obj.get("inputs") is not None else None ), "outputs": ( - dict((_k, OutputDescription.from_dict(_v)) for _k, _v in obj.get("outputs").items()) + dict( + (_k, OutputDescription.from_dict(_v)) + for _k, _v in obj.get("outputs").items() + ) if obj.get("outputs") is not None else None ), diff --git a/app/src/unity_sps_ogc_processes_api/models/process_summary.py b/app/src/unity_sps_ogc_processes_api/models/process_summary.py index 175d13c..d68436a 100644 --- a/app/src/unity_sps_ogc_processes_api/models/process_summary.py +++ b/app/src/unity_sps_ogc_processes_api/models/process_summary.py @@ -43,7 +43,9 @@ class ProcessSummary(BaseModel): metadata: Optional[List[Metadata]] = None id: StrictStr version: StrictStr - job_control_options: Optional[List[JobControlOptions]] = Field(default=None, alias="jobControlOptions") + job_control_options: Optional[List[JobControlOptions]] = Field( + default=None, alias="jobControlOptions" + ) links: Optional[List[Link]] = None __properties: ClassVar[List[str]] = [ "title", diff --git a/app/src/unity_sps_ogc_processes_api/models/qualified_input_value.py b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value.py index d25f160..d6f4769 100644 --- a/app/src/unity_sps_ogc_processes_api/models/qualified_input_value.py +++ b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value.py @@ -99,9 +99,15 @@ def from_dict(cls, obj: Dict) -> Self: "mediaType": obj.get("mediaType"), "encoding": obj.get("encoding"), "schema": ( - FormatSchema.from_dict(obj.get("schema")) if obj.get("schema") is not None else None + FormatSchema.from_dict(obj.get("schema")) + if obj.get("schema") is not None + else None + ), + "value": ( + InputValue.from_dict(obj.get("value")) + if obj.get("value") is not None + else None ), - "value": (InputValue.from_dict(obj.get("value")) if obj.get("value") is not None else None), } ) return _obj diff --git a/app/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py index d82bd76..e3301a5 100644 --- a/app/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py +++ b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py @@ -99,9 +99,15 @@ def from_dict(cls, obj: Dict) -> Self: "mediaType": obj.get("mediaType"), "encoding": obj.get("encoding"), "schema": ( - FormatSchema.from_dict(obj.get("schema")) if obj.get("schema") is not None else None + FormatSchema.from_dict(obj.get("schema")) + if obj.get("schema") is not None + else None + ), + "value": ( + InputValue1.from_dict(obj.get("value")) + if obj.get("value") is not None + else None ), - "value": (InputValue1.from_dict(obj.get("value")) if obj.get("value") is not None else None), } ) return _obj diff --git a/app/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py index 07feebd..c4a136d 100644 --- a/app/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py +++ b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value_workflows.py @@ -115,7 +115,9 @@ def from_dict(cls, obj: Dict) -> Self: "mediaType": obj.get("mediaType"), "encoding": obj.get("encoding"), "schema": ( - FormatSchema.from_dict(obj.get("schema")) if obj.get("schema") is not None else None + FormatSchema.from_dict(obj.get("schema")) + if obj.get("schema") is not None + else None ), "filter": obj.get("filter"), "properties": ( @@ -125,7 +127,9 @@ def from_dict(cls, obj: Dict) -> Self: ), "sortBy": obj.get("sortBy"), "value": ( - InputValueWorkflows.from_dict(obj.get("value")) if obj.get("value") is not None else None + InputValueWorkflows.from_dict(obj.get("value")) + if obj.get("value") is not None + else None ), } ) diff --git a/app/src/unity_sps_ogc_processes_api/models/schema1.py b/app/src/unity_sps_ogc_processes_api/models/schema1.py index 2c7ab5c..b31f8f1 100644 --- a/app/src/unity_sps_ogc_processes_api/models/schema1.py +++ b/app/src/unity_sps_ogc_processes_api/models/schema1.py @@ -53,9 +53,13 @@ class Schema1(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app/src/unity_sps_ogc_processes_api/models/schema_one_of.py b/app/src/unity_sps_ogc_processes_api/models/schema_one_of.py index 004c5b9..7ded79c 100644 --- a/app/src/unity_sps_ogc_processes_api/models/schema_one_of.py +++ b/app/src/unity_sps_ogc_processes_api/models/schema_one_of.py @@ -50,14 +50,26 @@ class SchemaOneOf(BaseModel): ] ] = Field(default=None, alias="multipleOf") maximum: Optional[Union[StrictFloat, StrictInt]] = None - exclusive_maximum: Optional[StrictBool] = Field(default=False, alias="exclusiveMaximum") + exclusive_maximum: Optional[StrictBool] = Field( + default=False, alias="exclusiveMaximum" + ) minimum: Optional[Union[StrictFloat, StrictInt]] = None - exclusive_minimum: Optional[StrictBool] = Field(default=False, alias="exclusiveMinimum") - max_length: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field(default=None, alias="maxLength") - min_length: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field(default=0, alias="minLength") + exclusive_minimum: Optional[StrictBool] = Field( + default=False, alias="exclusiveMinimum" + ) + max_length: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( + default=None, alias="maxLength" + ) + min_length: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( + default=0, alias="minLength" + ) pattern: Optional[StrictStr] = None - max_items: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field(default=None, alias="maxItems") - min_items: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field(default=0, alias="minItems") + max_items: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( + default=None, alias="maxItems" + ) + min_items: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( + default=0, alias="minItems" + ) unique_items: Optional[StrictBool] = Field(default=False, alias="uniqueItems") max_properties: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field( default=None, alias="maxProperties" @@ -85,7 +97,9 @@ class SchemaOneOf(BaseModel): write_only: Optional[StrictBool] = Field(default=False, alias="writeOnly") example: Optional[Dict[str, Any]] = None deprecated: Optional[StrictBool] = False - content_media_type: Optional[StrictStr] = Field(default=None, alias="contentMediaType") + content_media_type: Optional[StrictStr] = Field( + default=None, alias="contentMediaType" + ) content_encoding: Optional[StrictStr] = Field(default=None, alias="contentEncoding") content_schema: Optional[StrictStr] = Field(default=None, alias="contentSchema") __properties: ClassVar[List[str]] = [ @@ -227,24 +241,44 @@ def from_dict(cls, obj: Dict) -> Self: "multipleOf": obj.get("multipleOf"), "maximum": obj.get("maximum"), "exclusiveMaximum": ( - obj.get("exclusiveMaximum") if obj.get("exclusiveMaximum") is not None else False + obj.get("exclusiveMaximum") + if obj.get("exclusiveMaximum") is not None + else False ), "minimum": obj.get("minimum"), "exclusiveMinimum": ( - obj.get("exclusiveMinimum") if obj.get("exclusiveMinimum") is not None else False + obj.get("exclusiveMinimum") + if obj.get("exclusiveMinimum") is not None + else False ), "maxLength": obj.get("maxLength"), - "minLength": (obj.get("minLength") if obj.get("minLength") is not None else 0), + "minLength": ( + obj.get("minLength") if obj.get("minLength") is not None else 0 + ), "pattern": obj.get("pattern"), "maxItems": obj.get("maxItems"), - "minItems": (obj.get("minItems") if obj.get("minItems") is not None else 0), - "uniqueItems": (obj.get("uniqueItems") if obj.get("uniqueItems") is not None else False), + "minItems": ( + obj.get("minItems") if obj.get("minItems") is not None else 0 + ), + "uniqueItems": ( + obj.get("uniqueItems") + if obj.get("uniqueItems") is not None + else False + ), "maxProperties": obj.get("maxProperties"), - "minProperties": (obj.get("minProperties") if obj.get("minProperties") is not None else 0), + "minProperties": ( + obj.get("minProperties") + if obj.get("minProperties") is not None + else 0 + ), "required": obj.get("required"), "enum": obj.get("enum"), "type": obj.get("type"), - "not": (Schema1.from_dict(obj.get("not")) if obj.get("not") is not None else None), + "not": ( + Schema1.from_dict(obj.get("not")) + if obj.get("not") is not None + else None + ), "allOf": ( [Schema1.from_dict(_item) for _item in obj.get("allOf")] if obj.get("allOf") is not None @@ -260,25 +294,44 @@ def from_dict(cls, obj: Dict) -> Self: if obj.get("anyOf") is not None else None ), - "items": (Schema1.from_dict(obj.get("items")) if obj.get("items") is not None else None), + "items": ( + Schema1.from_dict(obj.get("items")) + if obj.get("items") is not None + else None + ), "properties": ( - dict((_k, Schema1.from_dict(_v)) for _k, _v in obj.get("properties").items()) + dict( + (_k, Schema1.from_dict(_v)) + for _k, _v in obj.get("properties").items() + ) if obj.get("properties") is not None else None ), "additionalProperties": ( - SchemaOneOfAdditionalProperties.from_dict(obj.get("additionalProperties")) + SchemaOneOfAdditionalProperties.from_dict( + obj.get("additionalProperties") + ) if obj.get("additionalProperties") is not None else None ), "description": obj.get("description"), "format": obj.get("format"), "default": obj.get("default"), - "nullable": (obj.get("nullable") if obj.get("nullable") is not None else False), - "readOnly": (obj.get("readOnly") if obj.get("readOnly") is not None else False), - "writeOnly": (obj.get("writeOnly") if obj.get("writeOnly") is not None else False), + "nullable": ( + obj.get("nullable") if obj.get("nullable") is not None else False + ), + "readOnly": ( + obj.get("readOnly") if obj.get("readOnly") is not None else False + ), + "writeOnly": ( + obj.get("writeOnly") if obj.get("writeOnly") is not None else False + ), "example": obj.get("example"), - "deprecated": (obj.get("deprecated") if obj.get("deprecated") is not None else False), + "deprecated": ( + obj.get("deprecated") + if obj.get("deprecated") is not None + else False + ), "contentMediaType": obj.get("contentMediaType"), "contentEncoding": obj.get("contentEncoding"), "contentSchema": obj.get("contentSchema"), diff --git a/app/src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py b/app/src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py index 3ae7829..19a6381 100644 --- a/app/src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py +++ b/app/src/unity_sps_ogc_processes_api/models/schema_one_of_additional_properties.py @@ -51,9 +51,13 @@ class SchemaOneOfAdditionalProperties(BaseModel): def __init__(self, *args, **kwargs) -> None: if args: if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") + raise ValueError( + "If a position argument is used, only 1 is allowed to set `actual_instance`" + ) if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") + raise ValueError( + "If a position argument is used, keyword arguments cannot be used." + ) super().__init__(actual_instance=args[0]) else: super().__init__(**kwargs) diff --git a/app/src/unity_sps_ogc_processes_api/models/static_indicator.py b/app/src/unity_sps_ogc_processes_api/models/static_indicator.py index cd2fb7f..24e3b14 100644 --- a/app/src/unity_sps_ogc_processes_api/models/static_indicator.py +++ b/app/src/unity_sps_ogc_processes_api/models/static_indicator.py @@ -43,7 +43,9 @@ class StaticIndicator(BaseModel): metadata: Optional[List[Metadata]] = None id: StrictStr version: StrictStr - job_control_options: Optional[List[JobControlOptions]] = Field(default=None, alias="jobControlOptions") + job_control_options: Optional[List[JobControlOptions]] = Field( + default=None, alias="jobControlOptions" + ) links: Optional[List[Link]] = None mutable: Optional[StrictBool] = True __properties: ClassVar[List[str]] = [ @@ -136,7 +138,9 @@ def from_dict(cls, obj: Dict) -> Self: if obj.get("links") is not None else None ), - "mutable": (obj.get("mutable") if obj.get("mutable") is not None else True), + "mutable": ( + obj.get("mutable") if obj.get("mutable") is not None else True + ), } ) return _obj diff --git a/app/src/unity_sps_ogc_processes_api/models/status_info.py b/app/src/unity_sps_ogc_processes_api/models/status_info.py index c8969b0..212d05c 100644 --- a/app/src/unity_sps_ogc_processes_api/models/status_info.py +++ b/app/src/unity_sps_ogc_processes_api/models/status_info.py @@ -137,7 +137,9 @@ def from_dict(cls, obj: Dict) -> Self: "status": obj.get("status"), "message": obj.get("message"), "exception": ( - Exception.from_dict(obj.get("exception")) if obj.get("exception") is not None else None + Exception.from_dict(obj.get("exception")) + if obj.get("exception") is not None + else None ), "created": obj.get("created"), "started": obj.get("started"), diff --git a/app/tests/conftest.py b/app/tests/conftest.py index 04cda12..6b98e63 100644 --- a/app/tests/conftest.py +++ b/app/tests/conftest.py @@ -61,14 +61,18 @@ def fake_filesystem(fs_session, test_directory): # pylint:disable=invalid-name """Variable name 'fs' causes a pylint warning. Provide a longer name acceptable to pylint for use in tests. """ - fs_session.add_real_directory(os.path.join(test_directory, "..", "..", "process_descriptions")) + fs_session.add_real_directory( + os.path.join(test_directory, "..", "..", "process_descriptions") + ) fs_session.add_real_directory(os.path.join(test_directory), "test_data") fs_session.create_dir(settings.DAG_CATALOG_DIRECTORY) fs_session.create_file( os.path.join(settings.DAG_CATALOG_DIRECTORY, "cwltool_help_dag.py"), contents="test", ) - fs_session.create_file(os.path.join(settings.DAG_CATALOG_DIRECTORY, "EchoProcess.py"), contents="test") + fs_session.create_file( + os.path.join(settings.DAG_CATALOG_DIRECTORY, "EchoProcess.py"), contents="test" + ) fs_session.create_dir(settings.DEPLOYED_DAGS_DIRECTORY) yield fs_session @@ -377,7 +381,9 @@ def mock_delete_existing_dag_dagrun(requests_mock): @pytest.fixture(scope="function", autouse=True) def mock_get_existing_running_dag_dagrun_tasks(requests_mock): return requests_mock.get( - re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)/taskInstances$"), + re.compile( + f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)/taskInstances$" + ), json={ "task_instances": [ { @@ -444,7 +450,9 @@ def mock_get_existing_running_dag_dagrun_tasks(requests_mock): @pytest.fixture(scope="function", autouse=True) def mock_patch_existing_running_dag_dagrun_task(requests_mock): return requests_mock.patch( - re.compile(f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)/taskInstances/([^/]*)$"), + re.compile( + f"{settings.EMS_API_URL}/dags/([^/]*)/dagRuns/([^/]*)/taskInstances/([^/]*)$" + ), json={ "task_id": "string", "dag_id": "string", @@ -456,7 +464,9 @@ def mock_patch_existing_running_dag_dagrun_task(requests_mock): @pytest.fixture(scope="function") def deploy_process(test_directory, client): - data_filename = os.path.join(test_directory, "..", "process_descriptions", "cwltool_help_dag.json") + data_filename = os.path.join( + test_directory, "..", "process_descriptions", "cwltool_help_dag.json" + ) f = open(data_filename) process_json = json.load(f) process = Process.model_validate(process_json)