Skip to content

Commit 2dfd86c

Browse files
committed
add remaining core endpoints to application
1 parent 6977afc commit 2dfd86c

File tree

11 files changed

+305
-12
lines changed

11 files changed

+305
-12
lines changed

openeo_fastapi/api/app.py

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def register_get_capabilities(self):
5050
)
5151

5252
def register_get_conformance(self):
53-
"""Register conformance page (GET /).
53+
"""Register conformance page (GET /conformance).
5454
Returns:
5555
None
5656
"""
@@ -65,12 +65,12 @@ def register_get_conformance(self):
6565
)
6666

6767
def register_get_file_formats(self):
68-
"""Register conformance page (GET /file_formats).
68+
"""Register supported file formats page (GET /file_formats).
6969
Returns:
7070
None
7171
"""
7272
self.router.add_api_route(
73-
name="conformance",
73+
name="file_formats",
7474
path=f"/{self.client.settings.OPENEO_VERSION}/file_formats",
7575
response_model=responses.FileFormatsGetResponse,
7676
response_model_exclude_unset=False,
@@ -79,6 +79,81 @@ def register_get_file_formats(self):
7979
endpoint=self.client.get_file_formats,
8080
)
8181

82+
def register_get_health(self):
83+
"""Register api health endpoint (GET /health).
84+
Returns:
85+
None
86+
"""
87+
self.router.add_api_route(
88+
name="health",
89+
path=f"/{self.client.settings.OPENEO_VERSION}/health",
90+
response_model=None,
91+
response_model_exclude_unset=False,
92+
response_model_exclude_none=True,
93+
methods=["GET"],
94+
endpoint=self.client.get_health,
95+
)
96+
97+
def register_get_user_info(self):
98+
"""Register conformance page (GET /me).
99+
Returns:
100+
None
101+
"""
102+
self.router.add_api_route(
103+
name="me",
104+
path=f"/{self.client.settings.OPENEO_VERSION}/me",
105+
response_model=responses.MeGetResponse,
106+
response_model_exclude_unset=False,
107+
response_model_exclude_none=True,
108+
methods=["GET"],
109+
endpoint=self.client.get_user_info,
110+
)
111+
112+
def register_get_udf_runtimes(self):
113+
"""Register supported udf runtimes (GET /udf_runtimes).
114+
Returns:
115+
None
116+
"""
117+
self.router.add_api_route(
118+
name="udf_runtimes",
119+
path=f"/{self.client.settings.OPENEO_VERSION}/udf_runtimes",
120+
response_model=responses.UdfRuntimesGetResponse,
121+
response_model_exclude_unset=False,
122+
response_model_exclude_none=True,
123+
methods=["GET"],
124+
endpoint=self.client.udf_runtimes,
125+
)
126+
127+
def register_validate_user_process_graph(self):
128+
"""Register validate user process graph (GET /validation).
129+
Returns:
130+
None
131+
"""
132+
self.router.add_api_route(
133+
name="validation",
134+
path=f"/{self.client.settings.OPENEO_VERSION}/validation",
135+
response_model=responses.ValidationPostResponse,
136+
response_model_exclude_unset=False,
137+
response_model_exclude_none=True,
138+
methods=["POST"],
139+
endpoint=self.client.processes.validate_user_process_graph,
140+
)
141+
142+
def register_run_sync_job(self):
143+
"""Register run synchronous job (GET /result).
144+
Returns:
145+
None
146+
"""
147+
self.router.add_api_route(
148+
name="result",
149+
path=f"/{self.client.settings.OPENEO_VERSION}/result",
150+
response_model=None,
151+
response_model_exclude_unset=False,
152+
response_model_exclude_none=True,
153+
methods=["POST"],
154+
endpoint=self.client.jobs.process_sync_job,
155+
)
156+
82157
def register_get_collections(self):
83158
"""Register collection Endpoint (GET /collections).
84159
Returns:
@@ -434,6 +509,11 @@ def register_core(self):
434509
None
435510
"""
436511
self.register_get_conformance()
512+
self.register_get_health()
513+
self.register_get_user_info()
514+
self.register_run_sync_job()
515+
self.register_get_udf_runtimes()
516+
self.register_validate_user_process_graph()
437517
self.register_get_file_formats()
438518
self.register_get_collections()
439519
self.register_get_collection()

openeo_fastapi/api/responses.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
from openeo_fastapi.api.types import (
88
Billing,
99
Endpoint,
10+
Error,
1011
File,
1112
FileFormat,
1213
Link,
1314
Process,
1415
RFC3339Datetime,
1516
Status,
17+
Storage1,
1618
Type1,
1719
Type2,
1820
Type5,
@@ -391,6 +393,10 @@ class ProcessGraphsGetResponse(BaseModel):
391393
links: list[Link]
392394

393395

396+
class ValidationPostResponse(BaseModel):
397+
errors: list[Error] = Field(..., description="A list of validation errors.")
398+
399+
394400
###########
395401
# Jobs
396402
###########
@@ -478,3 +484,49 @@ class FileFormatsGetResponse(BaseModel):
478484
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.",
479485
title="Output File Formats",
480486
)
487+
488+
489+
class MeGetResponse(BaseModel):
490+
user_id: uuid.UUID
491+
name: Optional[str] = Field(
492+
None,
493+
description="The user name, a human-friendly displayable name. Could be the user's real name or a nickname.",
494+
)
495+
default_plan: Optional[str] = Field(
496+
None,
497+
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.",
498+
example="free",
499+
)
500+
storage: Optional[Storage1] = Field(
501+
None,
502+
description="Information about the storage space available to the user.",
503+
title="User Storage",
504+
)
505+
budget: Optional[float] = None
506+
links: Optional[list[Link]] = Field(
507+
None,
508+
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).",
509+
example=[
510+
{"href": "https://example.openeo.org/john_doe/payment/", "rel": "payment"},
511+
{"href": "https://example.openeo.org/john_doe/edit/", "rel": "edit-form"},
512+
{
513+
"href": "https://example.openeo.org/john_doe/",
514+
"rel": "alternate",
515+
"type": "text/html",
516+
"title": "User profile",
517+
},
518+
{
519+
"href": "https://example.openeo.org/john_doe.vcf",
520+
"rel": "alternate",
521+
"type": "text/vcard",
522+
"title": "vCard of John Doe",
523+
},
524+
],
525+
)
526+
527+
528+
class UdfRuntimesGetResponse(BaseModel):
529+
pass
530+
531+
class Config:
532+
extra = Extra.allow

openeo_fastapi/api/types.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,3 +344,16 @@ class FileFormat(BaseModel):
344344
None,
345345
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).",
346346
)
347+
348+
349+
class Storage1(BaseModel):
350+
free: int = Field(
351+
...,
352+
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.",
353+
example=536870912,
354+
)
355+
quota: int = Field(
356+
...,
357+
description="Maximum storage space (disk quota) in bytes available to the user.",
358+
example=1073741824,
359+
)

openeo_fastapi/client/core.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
from urllib.parse import urlunparse
44

55
from attrs import define, field
6+
from fastapi import Depends, HTTPException, Response
67

7-
from openeo_fastapi.api import responses
8+
from openeo_fastapi.api import responses, types
89
from openeo_fastapi.client import conformance
10+
from openeo_fastapi.client.auth import Authenticator, User
911
from openeo_fastapi.client.collections import CollectionRegister
1012
from openeo_fastapi.client.files import FilesRegister
1113
from openeo_fastapi.client.jobs import JobsRegister
@@ -106,3 +108,22 @@ def get_file_formats(self) -> responses.FileFormatsGetResponse:
106108
input={_format.title: _format for _format in self.input_formats},
107109
output={_format.title: _format for _format in self.output_formats},
108110
)
111+
112+
def get_user_info(
113+
self, user: User = Depends(Authenticator.validate)
114+
) -> responses.MeGetResponse:
115+
""" """
116+
return responses.MeGetResponse(user_id=user.user_id.__str__())
117+
118+
def get_health(self):
119+
""" """
120+
return Response(status_code=200, content="OK")
121+
122+
def udf_runtimes(self) -> responses.UdfRuntimesGetResponse:
123+
""" """
124+
raise HTTPException(
125+
status_code=501,
126+
detail=types.Error(
127+
code="FeatureUnsupported", message="Feature not supported."
128+
),
129+
)

openeo_fastapi/client/jobs.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,3 +481,25 @@ def delete_job(
481481
status_code=501,
482482
detail=Error(code="FeatureUnsupported", message="Feature not supported."),
483483
)
484+
485+
def process_sync_job(self, user: User = Depends(Authenticator.validate)):
486+
"""_summary_
487+
488+
Args:
489+
job_id (JobId): _description_
490+
body (JobsRequest): _description_
491+
user (User): _description_
492+
493+
Raises:
494+
HTTPException: _description_
495+
HTTPException: _description_
496+
HTTPException: _description_
497+
HTTPException: _description_
498+
499+
Returns:
500+
_type_: _description_
501+
"""
502+
raise HTTPException(
503+
status_code=501,
504+
detail=Error(code="FeatureUnsupported", message="Feature not supported."),
505+
)

openeo_fastapi/client/processes.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
ProcessesGetResponse,
1414
ProcessGraphsGetResponse,
1515
ProcessGraphWithMetadata,
16+
ValidationPostResponse,
1617
)
1718
from openeo_fastapi.api.types import Endpoint, Error, Process
1819
from openeo_fastapi.client.auth import Authenticator, User
@@ -147,7 +148,10 @@ def get_user_process_graph(
147148
"""
148149
Lists all information about a user-defined process, including its process graph.
149150
"""
150-
graph = get(get_model=UserDefinedProcessGraph, primary_key=process_graph_id)
151+
graph = get(
152+
get_model=UserDefinedProcessGraph,
153+
primary_key=[process_graph_id, user.user_id],
154+
)
151155

152156
if not graph:
153157
raise HTTPException(
@@ -189,8 +193,14 @@ def delete_user_process_graph(
189193
"""
190194
Deletes the data related to this user-defined process, including its process graph.
191195
"""
192-
if get(get_model=UserDefinedProcessGraph, primary_key=process_graph_id):
193-
delete(delete_model=UserDefinedProcessGraph, primary_key=process_graph_id)
196+
if get(
197+
get_model=UserDefinedProcessGraph,
198+
primary_key=[process_graph_id, user.user_id],
199+
):
200+
delete(
201+
delete_model=UserDefinedProcessGraph,
202+
primary_key=[process_graph_id, user.user_id],
203+
)
194204
return Response(
195205
status_code=204,
196206
content="The user-defined process has been successfully deleted.",
@@ -202,3 +212,42 @@ def delete_user_process_graph(
202212
message=f"The requested resource {process_graph_id} was not found.",
203213
),
204214
)
215+
216+
def validate_user_process_graph(
217+
self,
218+
body: ProcessGraphWithMetadata,
219+
user: User = Depends(Authenticator.validate),
220+
) -> ValidationPostResponse:
221+
""" """
222+
from openeo_pg_parser_networkx.graph import OpenEOProcessGraph
223+
from openeo_pg_parser_networkx.resolving_utils import resolve_process_graph
224+
225+
def get_udp_spec(process_id: str, namespace: str):
226+
"""
227+
Get UDP spec
228+
"""
229+
if not namespace:
230+
raise PermissionError("No namespace given for UDP.")
231+
232+
udp = get(
233+
get_model=UserDefinedProcessGraph,
234+
primary_key=[process_id, namespace],
235+
)
236+
return udp.dict()
237+
238+
try:
239+
OpenEOProcessGraph(pg_data=body.process_graph)
240+
resolve_process_graph(
241+
process_graph=body.process_graph,
242+
process_registry=self.process_registry,
243+
get_udp_spec=get_udp_spec,
244+
namespace=user.user_id if user else "user",
245+
)
246+
except Exception as e:
247+
return Response(
248+
status_code=201,
249+
content=ValidationPostResponse(
250+
errors=[Error(code="Graph validation failed", message=f"{str(e)}")]
251+
).json(),
252+
)
253+
return ValidationPostResponse(errors=[])

openeo_fastapi/client/psql/engine.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,10 @@ def delete(delete_model: BaseModel, primary_key: Any) -> bool:
8888
db = sessionmaker(get_engine())
8989

9090
with db.begin() as session:
91-
delete_obj = session.get(delete_model.get_orm(), str(primary_key))
91+
if isinstance(primary_key, list):
92+
delete_obj = session.get(delete_model.get_orm(), primary_key)
93+
else:
94+
delete_obj = session.get(delete_model.get_orm(), str(primary_key))
9295

9396
session.delete(delete_obj)
9497
return True

openeo_fastapi/client/psql/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class UdpORM(BASE):
5353
__tablename__ = "udps"
5454

5555
id = Column(String, primary_key=True, nullable=False)
56-
user_id = Column(UUID(as_uuid=True), nullable=False)
56+
user_id = Column(UUID(as_uuid=True), primary_key=True, nullable=False)
5757
process_graph = Column(JSON, nullable=False)
5858
created = Column(DateTime, default=datetime.datetime.utcnow(), nullable=False)
5959
parameters = Column("parameters", JSON)

0 commit comments

Comments
 (0)