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/.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 diff --git a/Dockerfile b/Dockerfile index e9a8e43..3acdd65 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,12 +7,13 @@ 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 +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/.flake8 b/app/.flake8 new file mode 100644 index 0000000..9e008c5 --- /dev/null +++ b/app/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 88 +exclude = .git,__pycache__,__init__.py,.mypy_cache,.pytest_cache,.venv diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..a81c8ee --- /dev/null +++ b/app/.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/.openapi-generator-ignore b/app/.openapi-generator-ignore new file mode 100644 index 0000000..7484ee5 --- /dev/null +++ b/app/.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/.openapi-generator/FILES b/app/.openapi-generator/FILES new file mode 100644 index 0000000..d1ee7ca --- /dev/null +++ b/app/.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/.openapi-generator/VERSION b/app/.openapi-generator/VERSION new file mode 100644 index 0000000..1985849 --- /dev/null +++ b/app/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.7.0 diff --git a/app/Dockerfile b/app/Dockerfile new file mode 100644 index 0000000..b66458e --- /dev/null +++ b/app/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/README.md b/app/README.md new file mode 100644 index 0000000..a88a3ba --- /dev/null +++ b/app/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/database/crud.py b/app/database/crud.py deleted file mode 100644 index 210a78a..0000000 --- a/app/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/docker-compose.yaml b/app/docker-compose.yaml new file mode 100644 index 0000000..cf3a2ef --- /dev/null +++ b/app/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/main.py b/app/main.py deleted file mode 100644 index 7005533..0000000 --- a/app/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/openapi.yaml b/app/openapi.yaml new file mode 100644 index 0000000..b2efd19 --- /dev/null +++ b/app/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/pyproject.toml b/app/pyproject.toml new file mode 100644 index 0000000..67648d3 --- /dev/null +++ b/app/pyproject.toml @@ -0,0 +1,74 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "unity-sps-ogc-processes-api" +version = "2.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" +] + +[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"] +include = ["unity_sps_ogc_processes_api*", "openapi_server*"] + + +[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/schemas/ogc_processes.py b/app/schemas/ogc_processes.py deleted file mode 100644 index 99b104c..0000000 --- a/app/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/schemas/unity_sps.py b/app/schemas/unity_sps.py deleted file mode 100644 index ee53374..0000000 --- a/app/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/__init__.py b/app/src/openapi_server/config/__init__.py similarity index 100% rename from app/__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/database/__init__.py b/app/src/openapi_server/database/__init__.py similarity index 91% rename from app/database/__init__.py rename to app/src/openapi_server/database/__init__.py index 616c5c3..30d2541 100644 --- a/app/database/__init__.py +++ b/app/src/openapi_server/database/__init__.py @@ -1,7 +1,7 @@ from sqlalchemy import create_engine from sqlalchemy.orm import declarative_base, sessionmaker -from .. import config +from ..config import config settings = config.Settings() diff --git a/app/src/openapi_server/database/crud.py b/app/src/openapi_server/database/crud.py new file mode 100644 index 0000000..e88e249 --- /dev/null +++ b/app/src/openapi_server/database/crud.py @@ -0,0 +1,76 @@ +from sqlalchemy.orm import Session + +from unity_sps_ogc_processes_api.models.ogcapppkg import Ogcapppkg + +from . import models + + +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 + + +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_id: str): + db_process = db.query(models.Process).filter(models.Process.id == process_id).one() + db.delete(db_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/database/models.py b/app/src/openapi_server/database/models.py similarity index 51% rename from app/database/models.py rename to app/src/openapi_server/database/models.py index a13b29d..c24ee22 100644 --- a/app/database/models.py +++ b/app/src/openapi_server/database/models.py @@ -8,31 +8,67 @@ 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) + 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) + 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 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", ondelete="CASCADE")) + ogcapppkg = relationship("Ogcapppkg", back_populates="execution_unit") + + +class Ogcapppkg(Base): + __tablename__ = "ogcapppkgs" + _id = Column(Integer, primary_key=True) + 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, + 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") - results = relationship("Result", back_populates="job") - type = Column(String) status = Column(String) message = Column(String, nullable=True) @@ -43,16 +79,20 @@ class Job(Base): 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", + 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) diff --git a/app/schemas/__init__.py b/app/src/openapi_server/impl/__init__.py similarity index 100% rename from app/schemas/__init__.py rename to app/src/openapi_server/impl/__init__.py diff --git a/app/src/openapi_server/impl/api_api.py b/app/src/openapi_server/impl/api_api.py new file mode 100644 index 0000000..998b854 --- /dev/null +++ b/app/src/openapi_server/impl/api_api.py @@ -0,0 +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, f: str = None): + # Implementation for retrieving the API definition + return get_openapi( + title="OGC API - Processes", + version="1.0.0", + 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/src/openapi_server/impl/conformance_api.py b/app/src/openapi_server/impl/conformance_api.py new file mode 100644 index 0000000..13213ed --- /dev/null +++ b/app/src/openapi_server/impl/conformance_api.py @@ -0,0 +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): + 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/src/openapi_server/impl/dru_api.py b/app/src/openapi_server/impl/dru_api.py new file mode 100644 index 0000000..1a2a034 --- /dev/null +++ b/app/src/openapi_server/impl/dru_api.py @@ -0,0 +1,332 @@ +import os +import shutil +import time + +import requests +from fastapi import HTTPException, Response, status +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 + + +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: + 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 + ) + + 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) + ): + # TODO 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", + ) + + 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.", + ) + crud.create_process(self.db, ogcapppkg) + + return Response( + status_code=status.HTTP_201_CREATED, + content=f"Process {ogcapppkg.process_description.id} deployed successfully", + ) + except LockError: + raise HTTPException( + 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=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(lock_key, lock_timeout=60): + check_process_integrity(self.db, processId, new_process=False) + # 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", + ) + + # 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 LockError: + raise HTTPException( + 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=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) + + 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): + 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(), + ) + + # 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 + ) + + # 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, + 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.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) + + return Response( + status_code=status.HTTP_204_NO_CONTENT, + content=f"Process {processId} undeployed successfully", + ) + except LockError: + raise HTTPException( + 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=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/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/src/openapi_server/impl/jobs_api.py b/app/src/openapi_server/impl/jobs_api.py new file mode 100644 index 0000000..a16ad0c --- /dev/null +++ b/app/src/openapi_server/impl/jobs_api.py @@ -0,0 +1,209 @@ +from datetime import datetime +from typing import Dict + +import requests +from fastapi import HTTPException, status +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 +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 NoResultFound: + if not new_job: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Job with ID '{job_id}' not found", + ) + except MultipleResultsFound: + raise HTTPException( + 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=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_lock_key = f"job:{jobId}" + try: + 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): + 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 LockError: + raise HTTPException( + 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=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=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) + + def get_jobs(self) -> JobList: + jobs = crud.get_jobs(self.db) + 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=[], + ) + + 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): + 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): + results = crud.get_results(self.db, jobId) + return { + result.name: InlineOrRefData(href=result.href) + for result in results + } + + except LockError: + raise HTTPException( + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, + 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) + ) + + def get_status(self, jobId: str) -> StatusInfo: + job_lock_key = f"job:{jobId}" + try: + 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): + 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 LockError: + raise HTTPException( + 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=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=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 new file mode 100644 index 0000000..721d2d6 --- /dev/null +++ b/app/src/openapi_server/impl/landing_page_api.py @@ -0,0 +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, 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/src/openapi_server/impl/processes_api.py b/app/src/openapi_server/impl/processes_api.py new file mode 100644 index 0000000..aec7d6a --- /dev/null +++ b/app/src/openapi_server/impl/processes_api.py @@ -0,0 +1,217 @@ +import uuid +from datetime import datetime + +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 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 +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 __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: + lock_key = f"process:{processId}" + try: + with self.redis_locking_client.lock(lock_key): + 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, + ) + except LockError: + raise HTTPException( + status_code=status.HTTP_503_SERVICE_UNAVAILABLE, + 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) + ) + + def get_processes(self) -> ProcessList: + processes = crud.get_processes(self.db) + return ProcessList( + processes=[ + ProcessSummary( + 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 + ], + links=[], + ) + + def execute( + self, + processId: str, + execute_workflows: ExecuteWorkflows, + response: str, + prefer: str, + ) -> Execute200Response: + lock_key = f"process:{processId}" + try: + with self.redis_locking_client.lock(lock_key): + 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=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=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 LockError: + raise HTTPException( + 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 = 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) + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) + ) diff --git a/unity-test/__init__.py b/app/src/openapi_server/utils/__init__.py similarity index 100% rename from unity-test/__init__.py rename to app/src/openapi_server/utils/__init__.py diff --git a/app/redis.py b/app/src/openapi_server/utils/redis.py similarity index 66% rename from app/redis.py rename to app/src/openapi_server/utils/redis.py index 49851b3..6e9cdc3 100644 --- a/app/redis.py +++ b/app/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 diff --git a/app/src/unity_sps_ogc_processes_api/__init__.py b/app/src/unity_sps_ogc_processes_api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/src/unity_sps_ogc_processes_api/apis/__init__.py b/app/src/unity_sps_ogc_processes_api/apis/__init__.py new file mode 100644 index 0000000..e69de29 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 new file mode 100644 index 0000000..1bbdbdb --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/apis/api_api.py @@ -0,0 +1,72 @@ +# coding: utf-8 + +import importlib +import pkgutil + +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 + +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: + return BaseAPIApi.subclasses[0]().get_api(f) + + +@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: + return BaseAPIApi.subclasses[0]().get_api_processes(f) diff --git a/app/src/unity_sps_ogc_processes_api/apis/api_api_base.py b/app/src/unity_sps_ogc_processes_api/apis/api_api_base.py new file mode 100644 index 0000000..dde5ebf --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/apis/api_api_base.py @@ -0,0 +1,23 @@ +# coding: utf-8 + +from typing import ClassVar, Tuple + +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/src/unity_sps_ogc_processes_api/apis/conformance_api.py b/app/src/unity_sps_ogc_processes_api/apis/conformance_api.py new file mode 100644 index 0000000..2aa2bc7 --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/apis/conformance_api.py @@ -0,0 +1,44 @@ +# coding: utf-8 + +import importlib +import pkgutil + +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 + +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: + return BaseConformanceApi.subclasses[0]().get_conformance() diff --git a/app/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py b/app/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py new file mode 100644 index 0000000..7b93c60 --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/apis/conformance_api_base.py @@ -0,0 +1,18 @@ +# coding: utf-8 + +from typing import ClassVar, Tuple + +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/src/unity_sps_ogc_processes_api/apis/dru_api.py b/app/src/unity_sps_ogc_processes_api/apis/dru_api.py new file mode 100644 index 0000000..de701fa --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/apis/dru_api.py @@ -0,0 +1,124 @@ +# coding: utf-8 + +import importlib +import pkgutil + +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 + +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( + 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." + ), + 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).""" + dru_api = BaseDRUApi.subclasses[0](settings, redis_locking_client, db) + return dru_api.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( + 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).""" + dru_api = BaseDRUApi.subclasses[0](settings, redis_locking_client, db) + return dru_api.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.", + }, + 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"], + summary="undeploy a process.", + 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=""), + 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) + return dru_api.undeploy(processId, force) diff --git a/app/src/unity_sps_ogc_processes_api/apis/dru_api_base.py b/app/src/unity_sps_ogc_processes_api/apis/dru_api_base.py new file mode 100644 index 0000000..6e60ccf --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/apis/dru_api_base.py @@ -0,0 +1,36 @@ +# coding: utf-8 + +from typing import ClassVar, Tuple + +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/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..ce392cf --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/apis/health_api.py @@ -0,0 +1,33 @@ +# coding: utf-8 + +import importlib +import pkgutil + +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 + +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/src/unity_sps_ogc_processes_api/apis/jobs_api.py b/app/src/unity_sps_ogc_processes_api/apis/jobs_api.py new file mode 100644 index 0000000..2903053 --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/apis/jobs_api.py @@ -0,0 +1,134 @@ +# coding: utf-8 + +import importlib +import pkgutil +from typing import Dict + +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 +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( + 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).""" + jobs_api = BaseJobsApi.subclasses[0](settings, redis_locking_client, db) + return jobs_api.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( + 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).""" + jobs_api = BaseJobsApi.subclasses[0](settings, redis_locking_client, db) + return jobs_api.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( + 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, + 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).""" + jobs_api = BaseJobsApi.subclasses[0](settings, redis_locking_client, db) + return jobs_api.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( + 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).""" + jobs_api = BaseJobsApi.subclasses[0](settings, redis_locking_client, db) + return jobs_api.get_status(jobId) diff --git a/app/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py b/app/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py new file mode 100644 index 0000000..ec4bd3a --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/apis/jobs_api_base.py @@ -0,0 +1,43 @@ +# coding: utf-8 + +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 +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/src/unity_sps_ogc_processes_api/apis/landing_page_api.py b/app/src/unity_sps_ogc_processes_api/apis/landing_page_api.py new file mode 100644 index 0000000..b1e8e2c --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/apis/landing_page_api.py @@ -0,0 +1,44 @@ +# coding: utf-8 + +import importlib +import pkgutil + +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 + +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: + return BaseLandingPageApi.subclasses[0]().get_landing_page(f) diff --git a/app/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 new file mode 100644 index 0000000..ddceacd --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/apis/landing_page_api_base.py @@ -0,0 +1,18 @@ +# coding: utf-8 + +from typing import ClassVar, Tuple + +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/src/unity_sps_ogc_processes_api/apis/processes_api.py b/app/src/unity_sps_ogc_processes_api/apis/processes_api.py new file mode 100644 index 0000000..8354861 --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/apis/processes_api.py @@ -0,0 +1,123 @@ +# coding: utf-8 + +import importlib +import pkgutil + +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 +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( + 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, + 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).""" + processes_api = BaseProcessesApi.subclasses[0](settings, redis_locking_client, db) + return processes_api.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( + 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).""" + processes_api = BaseProcessesApi.subclasses[0](settings, redis_locking_client, db) + return processes_api.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( + 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).""" + processes_api = BaseProcessesApi.subclasses[0](settings, redis_locking_client, db) + return processes_api.get_processes() diff --git a/app/src/unity_sps_ogc_processes_api/apis/processes_api_base.py b/app/src/unity_sps_ogc_processes_api/apis/processes_api_base.py new file mode 100644 index 0000000..d4b8adc --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/apis/processes_api_base.py @@ -0,0 +1,39 @@ +# coding: utf-8 + +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 +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/src/unity_sps_ogc_processes_api/dependencies.py b/app/src/unity_sps_ogc_processes_api/dependencies.py new file mode 100644 index 0000000..027171b --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/main.py b/app/src/unity_sps_ogc_processes_api/main.py new file mode 100644 index 0000000..c917617 --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/main.py @@ -0,0 +1,63 @@ +# 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 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.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.processes_api import router as ProcessesApiRouter +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) + +app = FastAPI( + 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) +app.include_router(ConformanceApiRouter) +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) +app.include_router(HealthApiRouter) diff --git a/app/src/unity_sps_ogc_processes_api/models/__init__.py b/app/src/unity_sps_ogc_processes_api/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/src/unity_sps_ogc_processes_api/models/bbox.py b/app/src/unity_sps_ogc_processes_api/models/bbox.py new file mode 100644 index 0000000..2c49ce6 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/bbox1.py b/app/src/unity_sps_ogc_processes_api/models/bbox1.py new file mode 100644 index 0000000..c85ff66 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py b/app/src/unity_sps_ogc_processes_api/models/bbox_def_crs.py new file mode 100644 index 0000000..9c415b7 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/bbox_processes.py b/app/src/unity_sps_ogc_processes_api/models/bbox_processes.py new file mode 100644 index 0000000..0f5a692 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/collection_info.py b/app/src/unity_sps_ogc_processes_api/models/collection_info.py new file mode 100644 index 0000000..72bece4 --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..028fc9d --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/collections.py b/app/src/unity_sps_ogc_processes_api/models/collections.py new file mode 100644 index 0000000..4dd6775 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/conf_classes.py b/app/src/unity_sps_ogc_processes_api/models/conf_classes.py new file mode 100644 index 0000000..09ac3da --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/crs.py b/app/src/unity_sps_ogc_processes_api/models/crs.py new file mode 100644 index 0000000..47a3fab --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/crs_one_of.py b/app/src/unity_sps_ogc_processes_api/models/crs_one_of.py new file mode 100644 index 0000000..e059e18 --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..bb018ae --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..067c724 --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..927e4a9 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/data_type.py b/app/src/unity_sps_ogc_processes_api/models/data_type.py new file mode 100644 index 0000000..c530760 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/description_type.py b/app/src/unity_sps_ogc_processes_api/models/description_type.py new file mode 100644 index 0000000..305fecf --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/enumeration.py b/app/src/unity_sps_ogc_processes_api/models/enumeration.py new file mode 100644 index 0000000..bebfa57 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/exception.py b/app/src/unity_sps_ogc_processes_api/models/exception.py new file mode 100644 index 0000000..18d4451 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/execute.py b/app/src/unity_sps_ogc_processes_api/models/execute.py new file mode 100644 index 0000000..ce1da08 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/execute200_response.py b/app/src/unity_sps_ogc_processes_api/models/execute200_response.py new file mode 100644 index 0000000..77f0081 --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/models/execute200_response.py @@ -0,0 +1,76 @@ +# 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 re # noqa: F401 +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 + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Execute200Response(RootModel): + root: Union[ + Dict[ + str, + Union[Bbox, List[Any], bool, float, int, str, Link, QualifiedInputValue], + ], + List[Any], + bool, + FilePath, + float, + int, + Dict[str, Any], + str, + ] + + @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_dict(cls, obj: Dict[str, Any]) -> Self: + return cls(root=obj) + + def to_dict(self) -> Dict[str, Any]: + return self.root if isinstance(self.root, dict) else {"value": self.root} + + 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 __repr__(self) -> str: + return f"Execute200Response({self.root!r})" 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 new file mode 100644 index 0000000..7392912 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/execute_workflows.py b/app/src/unity_sps_ogc_processes_api/models/execute_workflows.py new file mode 100644 index 0000000..fbe895c --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/execute_workflows1.py b/app/src/unity_sps_ogc_processes_api/models/execute_workflows1.py new file mode 100644 index 0000000..0336057 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/execution_unit.py b/app/src/unity_sps_ogc_processes_api/models/execution_unit.py new file mode 100644 index 0000000..db04341 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/execution_unit_config.py b/app/src/unity_sps_ogc_processes_api/models/execution_unit_config.py new file mode 100644 index 0000000..8ef1b2e --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/extent.py b/app/src/unity_sps_ogc_processes_api/models/extent.py new file mode 100644 index 0000000..f5f48ee --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/extent_spatial.py b/app/src/unity_sps_ogc_processes_api/models/extent_spatial.py new file mode 100644 index 0000000..15666e6 --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..d943b10 --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..40e0685 --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..4f87b6c --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/extent_temporal.py b/app/src/unity_sps_ogc_processes_api/models/extent_temporal.py new file mode 100644 index 0000000..a30bbec --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py b/app/src/unity_sps_ogc_processes_api/models/extent_temporal_grid.py new file mode 100644 index 0000000..8b24a5b --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..6164e15 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/extent_uad.py b/app/src/unity_sps_ogc_processes_api/models/extent_uad.py new file mode 100644 index 0000000..6a09966 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/extra_models.py b/app/src/unity_sps_ogc_processes_api/models/extra_models.py new file mode 100644 index 0000000..f0588d2 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/feature_collection.py b/app/src/unity_sps_ogc_processes_api/models/feature_collection.py new file mode 100644 index 0000000..d9d2f08 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/fields_modifiers.py b/app/src/unity_sps_ogc_processes_api/models/fields_modifiers.py new file mode 100644 index 0000000..9b71705 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py b/app/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py new file mode 100644 index 0000000..1546bfe --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/models/fields_modifiers_properties.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +import json +import pprint +from typing import Any, Dict, List, Union + +from pydantic import RootModel, StrictStr, model_validator + + +class FieldsModifiersProperties(RootModel): + root: Union[Dict[str, StrictStr], List[StrictStr]] + + @model_validator(mode="before") + @classmethod + 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_dict(cls, obj: Dict[str, Any]) -> FieldsModifiersProperties: + return cls(root=cls.validate_type(obj)) + + @classmethod + def from_json(cls, json_str: str) -> FieldsModifiersProperties: + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + return self.root + + def to_json(self) -> str: + return json.dumps(self.to_dict()) + + def __getattr__(self, name: str) -> Any: + return getattr(self.root, name) + + def __repr__(self) -> str: + return f"FieldsModifiersProperties({self.root!r})" + + def to_str(self) -> str: + return pprint.pformat(self.model_dump()) diff --git a/app/src/unity_sps_ogc_processes_api/models/format.py b/app/src/unity_sps_ogc_processes_api/models/format.py new file mode 100644 index 0000000..8079d8d --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/format_schema.py b/app/src/unity_sps_ogc_processes_api/models/format_schema.py new file mode 100644 index 0000000..f0f0067 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/geo_json_feature.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_feature.py new file mode 100644 index 0000000..ce8da2e --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..557cefd --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..7062157 --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..5e38591 --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..4e13cb8 --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..8d64573 --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..56775ac --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/geo_json_point.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_point.py new file mode 100644 index 0000000..c8eb97f --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py b/app/src/unity_sps_ogc_processes_api/models/geo_json_polygon.py new file mode 100644 index 0000000..c535824 --- /dev/null +++ b/app/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/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..cd967f4 --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/models/health_check.py @@ -0,0 +1,7 @@ +from typing import Literal + +from pydantic import BaseModel + + +class HealthCheck(BaseModel): + status: Literal["OK"] 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 new file mode 100644 index 0000000..2bba538 --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/models/inline_or_ref_data.py @@ -0,0 +1,74 @@ +# 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 +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 + + +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[str, Any]) -> InlineOrRefData: + return cls(root=cls.validate_type(obj)) + + @classmethod + 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: + 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: + return pprint.pformat(self.model_dump()) 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 new file mode 100644 index 0000000..0bf5526 --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..7714621 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/input.py b/app/src/unity_sps_ogc_processes_api/models/input.py new file mode 100644 index 0000000..f0c3271 --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/models/input.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +import json +import pprint +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, +) + + +class Input(RootModel): + 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): + 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): + 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)}") + + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> Input: + return cls(root=cls.validate_type(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Input: + 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: + return json.dumps(self.to_dict()) + + def __getattr__(self, name: str) -> Any: + return getattr(self.root, name) + + def __repr__(self) -> str: + return f"Input({self.root!r})" + + def to_str(self) -> str: + return pprint.pformat(self.model_dump()) 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 new file mode 100644 index 0000000..fa243d3 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/input_description.py b/app/src/unity_sps_ogc_processes_api/models/input_description.py new file mode 100644 index 0000000..3cda30e --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/models/input_description.py @@ -0,0 +1,159 @@ +# 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[StrictInt] = Field(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/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 new file mode 100644 index 0000000..20d2005 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/input_parameterized.py b/app/src/unity_sps_ogc_processes_api/models/input_parameterized.py new file mode 100644 index 0000000..71b58f8 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/input_process.py b/app/src/unity_sps_ogc_processes_api/models/input_process.py new file mode 100644 index 0000000..e80c881 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/input_value.py b/app/src/unity_sps_ogc_processes_api/models/input_value.py new file mode 100644 index 0000000..aec7958 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/input_value1.py b/app/src/unity_sps_ogc_processes_api/models/input_value1.py new file mode 100644 index 0000000..e51aafd --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..fc969fd --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..9dd2681 --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..fc49e7e --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/input_value_workflows.py b/app/src/unity_sps_ogc_processes_api/models/input_value_workflows.py new file mode 100644 index 0000000..6d18d3a --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/input_workflows.py b/app/src/unity_sps_ogc_processes_api/models/input_workflows.py new file mode 100644 index 0000000..65bb11b --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/models/input_workflows.py @@ -0,0 +1,130 @@ +# 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 +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, +) + + +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_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[str, Any]) -> InputWorkflows: + return cls(root=cls.validate_type(obj)) + + @classmethod + 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: + 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: + return pprint.pformat(self.model_dump()) 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 new file mode 100644 index 0000000..6dd9a1e --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/job_control_options.py b/app/src/unity_sps_ogc_processes_api/models/job_control_options.py new file mode 100644 index 0000000..da5d6d9 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/job_list.py b/app/src/unity_sps_ogc_processes_api/models/job_list.py new file mode 100644 index 0000000..9fe2f2f --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/landing_page.py b/app/src/unity_sps_ogc_processes_api/models/landing_page.py new file mode 100644 index 0000000..2fffd7f --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/link.py b/app/src/unity_sps_ogc_processes_api/models/link.py new file mode 100644 index 0000000..94ddded --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/metadata.py b/app/src/unity_sps_ogc_processes_api/models/metadata.py new file mode 100644 index 0000000..963f702 --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/models/metadata.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +import json +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 + + +class Metadata(RootModel): + root: Union[MetadataOneOf, MetadataOneOf1] + + @model_validator(mode="before") + @classmethod + 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_dict(cls, obj: Dict[str, Any]) -> Metadata: + return cls(root=cls.validate_type(obj)) + + @classmethod + def from_json(cls, json_str: str) -> Metadata: + 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: + return json.dumps(self.to_dict()) + + def __getattr__(self, name: str) -> Any: + return getattr(self.root, name) + + def __repr__(self) -> str: + return f"Metadata({self.root!r})" diff --git a/app/src/unity_sps_ogc_processes_api/models/metadata_one_of.py b/app/src/unity_sps_ogc_processes_api/models/metadata_one_of.py new file mode 100644 index 0000000..addb403 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py b/app/src/unity_sps_ogc_processes_api/models/metadata_one_of1.py new file mode 100644 index 0000000..cc3eeb3 --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..2930109 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/model_schema.py b/app/src/unity_sps_ogc_processes_api/models/model_schema.py new file mode 100644 index 0000000..a9e7f42 --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/models/model_schema.py @@ -0,0 +1,67 @@ +# 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 +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 + + +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[str, Any]) -> ModelSchema: + return cls(root=cls.validate_type(obj)) + + @classmethod + 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: + 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: + return pprint.pformat(self.model_dump()) diff --git a/app/src/unity_sps_ogc_processes_api/models/ogcapppkg.py b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg.py new file mode 100644 index 0000000..5c5d2c3 --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg.py @@ -0,0 +1,110 @@ +# 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 +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.process import Process + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + + +class Ogcapppkg(BaseModel): + """ + Ogcapppkg + """ # noqa: E501 + + process_description: Process = Field(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/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_array_inner.py new file mode 100644 index 0000000..eea9993 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py new file mode 100644 index 0000000..2448633 --- /dev/null +++ b/app/src/unity_sps_ogc_processes_api/models/ogcapppkg_execution_unit.py @@ -0,0 +1,75 @@ +# 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 +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 + + +class OgcapppkgExecutionUnit(RootModel): + root: Union[ + ExecutionUnit, + Link, + QualifiedInputValue, + List[Union[ExecutionUnit, Link, 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 + elif isinstance(value, list): + return [cls.validate_type(item) for item in value] + elif isinstance(value, (ExecutionUnit, Link, QualifiedInputValue)): + return value + raise ValueError(f"Invalid type for OgcapppkgExecutionUnit: {type(value)}") + + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> OgcapppkgExecutionUnit: + return cls(root=cls.validate_type(obj)) + + @classmethod + 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: + 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: + return pprint.pformat(self.model_dump()) diff --git a/app/src/unity_sps_ogc_processes_api/models/output.py b/app/src/unity_sps_ogc_processes_api/models/output.py new file mode 100644 index 0000000..bfc5305 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/output_description.py b/app/src/unity_sps_ogc_processes_api/models/output_description.py new file mode 100644 index 0000000..7b52c03 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/output_workflows.py b/app/src/unity_sps_ogc_processes_api/models/output_workflows.py new file mode 100644 index 0000000..7431654 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/output_workflows1.py b/app/src/unity_sps_ogc_processes_api/models/output_workflows1.py new file mode 100644 index 0000000..831e686 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/process.py b/app/src/unity_sps_ogc_processes_api/models/process.py new file mode 100644 index 0000000..af787f9 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/process_list.py b/app/src/unity_sps_ogc_processes_api/models/process_list.py new file mode 100644 index 0000000..d7ac593 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/process_summary.py b/app/src/unity_sps_ogc_processes_api/models/process_summary.py new file mode 100644 index 0000000..d68436a --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/processes_list.py b/app/src/unity_sps_ogc_processes_api/models/processes_list.py new file mode 100644 index 0000000..f010719 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/qualified_input_value.py b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value.py new file mode 100644 index 0000000..d6f4769 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py b/app/src/unity_sps_ogc_processes_api/models/qualified_input_value1.py new file mode 100644 index 0000000..e3301a5 --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..c4a136d --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/reference.py b/app/src/unity_sps_ogc_processes_api/models/reference.py new file mode 100644 index 0000000..7e4745b --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/schema1.py b/app/src/unity_sps_ogc_processes_api/models/schema1.py new file mode 100644 index 0000000..b31f8f1 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/schema_one_of.py b/app/src/unity_sps_ogc_processes_api/models/schema_one_of.py new file mode 100644 index 0000000..7ded79c --- /dev/null +++ b/app/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/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 new file mode 100644 index 0000000..19a6381 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/static_indicator.py b/app/src/unity_sps_ogc_processes_api/models/static_indicator.py new file mode 100644 index 0000000..24e3b14 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/status_code.py b/app/src/unity_sps_ogc_processes_api/models/status_code.py new file mode 100644 index 0000000..2a873fa --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/status_info.py b/app/src/unity_sps_ogc_processes_api/models/status_info.py new file mode 100644 index 0000000..212d05c --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/models/subscriber.py b/app/src/unity_sps_ogc_processes_api/models/subscriber.py new file mode 100644 index 0000000..d67d0a0 --- /dev/null +++ b/app/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/src/unity_sps_ogc_processes_api/security_api.py b/app/src/unity_sps_ogc_processes_api/security_api.py new file mode 100644 index 0000000..dfc6b32 --- /dev/null +++ b/app/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/unity-test/conftest.py b/app/tests/conftest.py similarity index 83% rename from unity-test/conftest.py rename to app/tests/conftest.py index b983019..6b98e63 100644 --- a/unity-test/conftest.py +++ b/app/tests/conftest.py @@ -1,22 +1,26 @@ -import glob import json import os -import pathlib import re 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 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 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 @@ -29,11 +33,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() @@ -62,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" + 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_file(os.path.join(settings.DAG_CATALOG_DIRECTORY, "cwl_dag.py"), contents="test") fs_session.create_dir(settings.DEPLOYED_DAGS_DIRECTORY) yield fs_session @@ -102,7 +105,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, @@ -133,7 +141,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, @@ -173,7 +186,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, @@ -204,7 +222,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, @@ -224,7 +247,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, ) @@ -357,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": [ { @@ -424,21 +450,31 @@ 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/([^/]*)$"), - json={"task_id": "string", "dag_id": "string", "execution_date": "string", "dag_run_id": "string"}, + 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", 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 @@ -446,20 +482,27 @@ def deploy_process(test_directory, client, request): assert response.status_code == status.HTTP_204_NO_CONTENT -@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)) - 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/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/tests/test_api_api.py b/app/tests/test_api_api.py new file mode 100644 index 0000000..8e29116 --- /dev/null +++ b/app/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/tests/test_conformance_api.py b/app/tests/test_conformance_api.py new file mode 100644 index 0000000..99d2372 --- /dev/null +++ b/app/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/tests/test_dru_api.py b/app/tests/test_dru_api.py new file mode 100644 index 0000000..d0188e1 --- /dev/null +++ b/app/tests/test_dru_api.py @@ -0,0 +1,74 @@ +# 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/app/tests/test_jobs_api.py b/app/tests/test_jobs_api.py new file mode 100644 index 0000000..522bedf --- /dev/null +++ b/app/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/tests/test_landing_page_api.py b/app/tests/test_landing_page_api.py new file mode 100644 index 0000000..d092aee --- /dev/null +++ b/app/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/tests/test_processes_api.py b/app/tests/test_processes_api.py new file mode 100644 index 0000000..914bdd0 --- /dev/null +++ b/app/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/ogcapi-processes b/ogcapi-processes index d011fe9..b37c67f 160000 --- a/ogcapi-processes +++ b/ogcapi-processes @@ -1 +1 @@ -Subproject commit d011fe92750a0843106fcb87b6292db6e2b0fa51 +Subproject commit b37c67f1aecc6c16e5b47702cd9db4e9611fe0b8 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 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] 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