Skip to content

Commit

Permalink
add remaining core endpoints to application (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
SerRichard authored Mar 27, 2024
1 parent 6977afc commit bbfae14
Show file tree
Hide file tree
Showing 11 changed files with 305 additions and 12 deletions.
86 changes: 83 additions & 3 deletions openeo_fastapi/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def register_get_capabilities(self):
)

def register_get_conformance(self):
"""Register conformance page (GET /).
"""Register conformance page (GET /conformance).
Returns:
None
"""
Expand All @@ -65,12 +65,12 @@ def register_get_conformance(self):
)

def register_get_file_formats(self):
"""Register conformance page (GET /file_formats).
"""Register supported file formats page (GET /file_formats).
Returns:
None
"""
self.router.add_api_route(
name="conformance",
name="file_formats",
path=f"/{self.client.settings.OPENEO_VERSION}/file_formats",
response_model=responses.FileFormatsGetResponse,
response_model_exclude_unset=False,
Expand All @@ -79,6 +79,81 @@ def register_get_file_formats(self):
endpoint=self.client.get_file_formats,
)

def register_get_health(self):
"""Register api health endpoint (GET /health).
Returns:
None
"""
self.router.add_api_route(
name="health",
path=f"/{self.client.settings.OPENEO_VERSION}/health",
response_model=None,
response_model_exclude_unset=False,
response_model_exclude_none=True,
methods=["GET"],
endpoint=self.client.get_health,
)

def register_get_user_info(self):
"""Register conformance page (GET /me).
Returns:
None
"""
self.router.add_api_route(
name="me",
path=f"/{self.client.settings.OPENEO_VERSION}/me",
response_model=responses.MeGetResponse,
response_model_exclude_unset=False,
response_model_exclude_none=True,
methods=["GET"],
endpoint=self.client.get_user_info,
)

def register_get_udf_runtimes(self):
"""Register supported udf runtimes (GET /udf_runtimes).
Returns:
None
"""
self.router.add_api_route(
name="udf_runtimes",
path=f"/{self.client.settings.OPENEO_VERSION}/udf_runtimes",
response_model=responses.UdfRuntimesGetResponse,
response_model_exclude_unset=False,
response_model_exclude_none=True,
methods=["GET"],
endpoint=self.client.udf_runtimes,
)

def register_validate_user_process_graph(self):
"""Register validate user process graph (GET /validation).
Returns:
None
"""
self.router.add_api_route(
name="validation",
path=f"/{self.client.settings.OPENEO_VERSION}/validation",
response_model=responses.ValidationPostResponse,
response_model_exclude_unset=False,
response_model_exclude_none=True,
methods=["POST"],
endpoint=self.client.processes.validate_user_process_graph,
)

def register_run_sync_job(self):
"""Register run synchronous job (GET /result).
Returns:
None
"""
self.router.add_api_route(
name="result",
path=f"/{self.client.settings.OPENEO_VERSION}/result",
response_model=None,
response_model_exclude_unset=False,
response_model_exclude_none=True,
methods=["POST"],
endpoint=self.client.jobs.process_sync_job,
)

def register_get_collections(self):
"""Register collection Endpoint (GET /collections).
Returns:
Expand Down Expand Up @@ -434,6 +509,11 @@ def register_core(self):
None
"""
self.register_get_conformance()
self.register_get_health()
self.register_get_user_info()
self.register_run_sync_job()
self.register_get_udf_runtimes()
self.register_validate_user_process_graph()
self.register_get_file_formats()
self.register_get_collections()
self.register_get_collection()
Expand Down
52 changes: 52 additions & 0 deletions openeo_fastapi/api/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
from openeo_fastapi.api.types import (
Billing,
Endpoint,
Error,
File,
FileFormat,
Link,
Process,
RFC3339Datetime,
Status,
Storage1,
Type1,
Type2,
Type5,
Expand Down Expand Up @@ -391,6 +393,10 @@ class ProcessGraphsGetResponse(BaseModel):
links: list[Link]


class ValidationPostResponse(BaseModel):
errors: list[Error] = Field(..., description="A list of validation errors.")


###########
# Jobs
###########
Expand Down Expand Up @@ -478,3 +484,49 @@ class FileFormatsGetResponse(BaseModel):
description="Map of supported output file formats, i.e. file formats a back-end can **write** to. The property keys are the file format names that are used by clients and users, for example in process graphs.",
title="Output File Formats",
)


class MeGetResponse(BaseModel):
user_id: uuid.UUID
name: Optional[str] = Field(
None,
description="The user name, a human-friendly displayable name. Could be the user's real name or a nickname.",
)
default_plan: Optional[str] = Field(
None,
description="Name of the plan the user has subscribed to.\n\nOverrides the default plan of the back-end, but back-ends\nMAY also allow overriding this plan for each individual\nprocessing request (e.g. job or service) with the\ncorresponding `plan` property.",
example="free",
)
storage: Optional[Storage1] = Field(
None,
description="Information about the storage space available to the user.",
title="User Storage",
)
budget: Optional[float] = None
links: Optional[list[Link]] = Field(
None,
description="Links related to the user profile, e.g. where payments\nare handled or the user profile could be edited.\n\nIt is RECOMMENDED to provide links with the following `rel` (relation) types:\n\n1. `payment`: A page where users can recharge their user account with money or credits.\n\n2. `edit-form`: Points to a page where the user can edit his user profile.\n\nFor additional relation types see also the lists of\n[common relation types in openEO](#section/API-Principles/Web-Linking).",
example=[
{"href": "https://example.openeo.org/john_doe/payment/", "rel": "payment"},
{"href": "https://example.openeo.org/john_doe/edit/", "rel": "edit-form"},
{
"href": "https://example.openeo.org/john_doe/",
"rel": "alternate",
"type": "text/html",
"title": "User profile",
},
{
"href": "https://example.openeo.org/john_doe.vcf",
"rel": "alternate",
"type": "text/vcard",
"title": "vCard of John Doe",
},
],
)


class UdfRuntimesGetResponse(BaseModel):
pass

class Config:
extra = Extra.allow
13 changes: 13 additions & 0 deletions openeo_fastapi/api/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,3 +344,16 @@ class FileFormat(BaseModel):
None,
description="Links related to this file format, e.g. external documentation.\n\nFor relation types see the lists of\n[common relation types in openEO](#section/API-Principles/Web-Linking).",
)


class Storage1(BaseModel):
free: int = Field(
...,
description="Free storage space in bytes, which is still available to the user. Effectively, this is the disk quota minus the used space by the user, e.g. user-uploaded files and job results.",
example=536870912,
)
quota: int = Field(
...,
description="Maximum storage space (disk quota) in bytes available to the user.",
example=1073741824,
)
23 changes: 22 additions & 1 deletion openeo_fastapi/client/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from urllib.parse import urlunparse

from attrs import define, field
from fastapi import Depends, HTTPException, Response

from openeo_fastapi.api import responses
from openeo_fastapi.api import responses, types
from openeo_fastapi.client import conformance
from openeo_fastapi.client.auth import Authenticator, User
from openeo_fastapi.client.collections import CollectionRegister
from openeo_fastapi.client.files import FilesRegister
from openeo_fastapi.client.jobs import JobsRegister
Expand Down Expand Up @@ -106,3 +108,22 @@ def get_file_formats(self) -> responses.FileFormatsGetResponse:
input={_format.title: _format for _format in self.input_formats},
output={_format.title: _format for _format in self.output_formats},
)

def get_user_info(
self, user: User = Depends(Authenticator.validate)
) -> responses.MeGetResponse:
""" """
return responses.MeGetResponse(user_id=user.user_id.__str__())

def get_health(self):
""" """
return Response(status_code=200, content="OK")

def udf_runtimes(self) -> responses.UdfRuntimesGetResponse:
""" """
raise HTTPException(
status_code=501,
detail=types.Error(
code="FeatureUnsupported", message="Feature not supported."
),
)
22 changes: 22 additions & 0 deletions openeo_fastapi/client/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,3 +481,25 @@ def delete_job(
status_code=501,
detail=Error(code="FeatureUnsupported", message="Feature not supported."),
)

def process_sync_job(self, user: User = Depends(Authenticator.validate)):
"""_summary_
Args:
job_id (JobId): _description_
body (JobsRequest): _description_
user (User): _description_
Raises:
HTTPException: _description_
HTTPException: _description_
HTTPException: _description_
HTTPException: _description_
Returns:
_type_: _description_
"""
raise HTTPException(
status_code=501,
detail=Error(code="FeatureUnsupported", message="Feature not supported."),
)
55 changes: 52 additions & 3 deletions openeo_fastapi/client/processes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
ProcessesGetResponse,
ProcessGraphsGetResponse,
ProcessGraphWithMetadata,
ValidationPostResponse,
)
from openeo_fastapi.api.types import Endpoint, Error, Process
from openeo_fastapi.client.auth import Authenticator, User
Expand Down Expand Up @@ -147,7 +148,10 @@ def get_user_process_graph(
"""
Lists all information about a user-defined process, including its process graph.
"""
graph = get(get_model=UserDefinedProcessGraph, primary_key=process_graph_id)
graph = get(
get_model=UserDefinedProcessGraph,
primary_key=[process_graph_id, user.user_id],
)

if not graph:
raise HTTPException(
Expand Down Expand Up @@ -189,8 +193,14 @@ def delete_user_process_graph(
"""
Deletes the data related to this user-defined process, including its process graph.
"""
if get(get_model=UserDefinedProcessGraph, primary_key=process_graph_id):
delete(delete_model=UserDefinedProcessGraph, primary_key=process_graph_id)
if get(
get_model=UserDefinedProcessGraph,
primary_key=[process_graph_id, user.user_id],
):
delete(
delete_model=UserDefinedProcessGraph,
primary_key=[process_graph_id, user.user_id],
)
return Response(
status_code=204,
content="The user-defined process has been successfully deleted.",
Expand All @@ -202,3 +212,42 @@ def delete_user_process_graph(
message=f"The requested resource {process_graph_id} was not found.",
),
)

def validate_user_process_graph(
self,
body: ProcessGraphWithMetadata,
user: User = Depends(Authenticator.validate),
) -> ValidationPostResponse:
""" """
from openeo_pg_parser_networkx.graph import OpenEOProcessGraph
from openeo_pg_parser_networkx.resolving_utils import resolve_process_graph

def get_udp_spec(process_id: str, namespace: str):
"""
Get UDP spec
"""
if not namespace:
raise PermissionError("No namespace given for UDP.")

udp = get(
get_model=UserDefinedProcessGraph,
primary_key=[process_id, namespace],
)
return udp.dict()

try:
OpenEOProcessGraph(pg_data=body.process_graph)
resolve_process_graph(
process_graph=body.process_graph,
process_registry=self.process_registry,
get_udp_spec=get_udp_spec,
namespace=user.user_id if user else "user",
)
except Exception as e:
return Response(
status_code=201,
content=ValidationPostResponse(
errors=[Error(code="Graph validation failed", message=f"{str(e)}")]
).json(),
)
return ValidationPostResponse(errors=[])
5 changes: 4 additions & 1 deletion openeo_fastapi/client/psql/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ def delete(delete_model: BaseModel, primary_key: Any) -> bool:
db = sessionmaker(get_engine())

with db.begin() as session:
delete_obj = session.get(delete_model.get_orm(), str(primary_key))
if isinstance(primary_key, list):
delete_obj = session.get(delete_model.get_orm(), primary_key)
else:
delete_obj = session.get(delete_model.get_orm(), str(primary_key))

session.delete(delete_obj)
return True
Expand Down
2 changes: 1 addition & 1 deletion openeo_fastapi/client/psql/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class UdpORM(BASE):
__tablename__ = "udps"

id = Column(String, primary_key=True, nullable=False)
user_id = Column(UUID(as_uuid=True), nullable=False)
user_id = Column(UUID(as_uuid=True), primary_key=True, nullable=False)
process_graph = Column(JSON, nullable=False)
created = Column(DateTime, default=datetime.datetime.utcnow(), nullable=False)
parameters = Column("parameters", JSON)
Expand Down
Loading

0 comments on commit bbfae14

Please sign in to comment.