Skip to content

Commit 9cab96f

Browse files
authored
Add jobs (#31)
* add basic register for the job * add basis for job handling * get tests parsing and get basic create job test to parse * big restructure to submodule structure per registry * restructure again! * stay on deprecated version for now * add job patch functionality and unit test * add unit test to check non default endpoints are hooked up to the api and returning the appropriate error
1 parent aedca75 commit 9cab96f

21 files changed

+1964
-1045
lines changed

openeo_fastapi/api/app.py

Lines changed: 191 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
import attr
2-
from attrs import define, field
32
from fastapi import APIRouter, HTTPException, Response
43
from starlette.responses import JSONResponse
54

6-
from openeo_fastapi.client import models
5+
from openeo_fastapi.api import responses
76

87
HIDDEN_PATHS = ["/openapi.json", "/docs", "/docs/oauth2-redirect", "/redoc"]
98

109

11-
@define
10+
@attr.define
1211
class OpenEOApi:
1312
"""Factory for creating FastApi applications conformant to the OpenEO Api specification."""
1413

15-
client: field
16-
app: field
14+
client: attr.field
15+
app: attr.field
1716
router: APIRouter = attr.ib(default=attr.Factory(APIRouter))
1817
response_class: type[Response] = attr.ib(default=JSONResponse)
1918

@@ -27,7 +26,7 @@ def register_well_known(self):
2726
self.router.add_api_route(
2827
name=".well-known",
2928
path="/.well-known/openeo",
30-
response_model=models.WellKnownOpeneoGetResponse,
29+
response_model=responses.WellKnownOpeneoGetResponse,
3130
response_model_exclude_unset=False,
3231
response_model_exclude_none=True,
3332
methods=["GET"],
@@ -43,13 +42,28 @@ def register_get_capabilities(self):
4342
self.router.add_api_route(
4443
name="capabilities",
4544
path=f"/{self.client.settings.OPENEO_VERSION}" + "/",
46-
response_model=models.Capabilities,
45+
response_model=responses.Capabilities,
4746
response_model_exclude_unset=False,
4847
response_model_exclude_none=True,
4948
methods=["GET"],
5049
endpoint=self.client.get_capabilities,
5150
)
5251

52+
def register_get_conformance(self):
53+
"""Register conformance page (GET /).
54+
Returns:
55+
None
56+
"""
57+
self.router.add_api_route(
58+
name="conformance",
59+
path=f"/{self.client.settings.OPENEO_VERSION}/conformance",
60+
response_model=responses.ConformanceGetResponse,
61+
response_model_exclude_unset=False,
62+
response_model_exclude_none=True,
63+
methods=["GET"],
64+
endpoint=self.client.get_conformance,
65+
)
66+
5367
def register_get_collections(self):
5468
"""Register collection Endpoint (GET /collections).
5569
Returns:
@@ -58,11 +72,11 @@ def register_get_collections(self):
5872
self.router.add_api_route(
5973
name="collections",
6074
path=f"/{self.client.settings.OPENEO_VERSION}/collections",
61-
response_model=None,
75+
response_model=responses.Collections,
6276
response_model_exclude_unset=False,
6377
response_model_exclude_none=True,
6478
methods=["GET"],
65-
endpoint=self.client.get_collections,
79+
endpoint=self.client._collections.get_collections,
6680
)
6781

6882
def register_get_collection(self):
@@ -74,42 +88,187 @@ def register_get_collection(self):
7488
name="collection",
7589
path=f"/{self.client.settings.OPENEO_VERSION}"
7690
+ "/collections/{collection_id}",
77-
response_model=None,
91+
response_model=responses.Collection,
7892
response_model_exclude_unset=False,
7993
response_model_exclude_none=True,
8094
methods=["GET"],
81-
endpoint=self.client.get_collection,
95+
endpoint=self.client._collections.get_collection,
8296
)
8397

84-
def register_get_conformance(self):
85-
"""Register conformance page (GET /).
98+
def register_get_processes(self):
99+
"""Register Endpoint for Processes (GET /processes).
100+
86101
Returns:
87102
None
88103
"""
89104
self.router.add_api_route(
90-
name="conformance",
91-
path=f"/{self.client.settings.OPENEO_VERSION}/conformance",
92-
response_model=models.ConformanceGetResponse,
105+
name="processes",
106+
path=f"/{self.client.settings.OPENEO_VERSION}/processes",
107+
response_model=responses.ProcessesGetResponse,
93108
response_model_exclude_unset=False,
94109
response_model_exclude_none=True,
95110
methods=["GET"],
96-
endpoint=self.client.get_conformance,
111+
endpoint=self.client._processes.list_processes,
97112
)
98113

99-
def register_get_processes(self):
100-
"""Register Endpoint for Processes (GET /processes).
114+
def register_get_jobs(self):
115+
"""Register Endpoint for Jobs (GET /jobs).
101116
102117
Returns:
103118
None
104119
"""
105120
self.router.add_api_route(
106-
name="processes",
107-
path=f"/{self.client.settings.OPENEO_VERSION}/processes",
121+
name="get_jobs",
122+
path=f"/{self.client.settings.OPENEO_VERSION}/jobs",
123+
response_model=responses.JobsGetResponse,
124+
response_model_exclude_unset=False,
125+
response_model_exclude_none=True,
126+
methods=["GET"],
127+
endpoint=self.client._jobs.list_jobs,
128+
)
129+
130+
def register_create_job(self):
131+
"""Register Endpoint for Jobs (POST /jobs).
132+
133+
Returns:
134+
None
135+
"""
136+
self.router.add_api_route(
137+
name="post_job",
138+
path=f"/{self.client.settings.OPENEO_VERSION}/jobs",
139+
response_model=None,
140+
response_model_exclude_unset=False,
141+
response_model_exclude_none=True,
142+
methods=["POST"],
143+
endpoint=self.client._jobs.create_job,
144+
)
145+
146+
def register_update_job(self):
147+
"""Register Endpoint for Jobs (POST /jobs/{job_id}).
148+
149+
Returns:
150+
None
151+
"""
152+
self.router.add_api_route(
153+
name="post_job",
154+
path=f"/{self.client.settings.OPENEO_VERSION}/jobs" + "/{job_id}",
108155
response_model=None,
109156
response_model_exclude_unset=False,
110157
response_model_exclude_none=True,
158+
methods=["PATCH"],
159+
endpoint=self.client._jobs.update_job,
160+
)
161+
162+
def register_get_job(self):
163+
"""Register Endpoint for Jobs (GET /jobs/{job_id}).
164+
165+
Returns:
166+
None
167+
"""
168+
self.router.add_api_route(
169+
name="get_job",
170+
path=f"/{self.client.settings.OPENEO_VERSION}/jobs" + "/{job_id}",
171+
response_model=responses.BatchJob,
172+
response_model_exclude_unset=False,
173+
response_model_exclude_none=True,
111174
methods=["GET"],
112-
endpoint=self.client.get_processes,
175+
endpoint=self.client._jobs.get_job,
176+
)
177+
178+
def register_delete_job(self):
179+
"""Register Endpoint for Jobs (GET /jobs/{job_id}).
180+
181+
Returns:
182+
None
183+
"""
184+
self.router.add_api_route(
185+
name="delete_job",
186+
path=f"/{self.client.settings.OPENEO_VERSION}/jobs" + "/{job_id}",
187+
response_model=None,
188+
response_model_exclude_unset=False,
189+
response_model_exclude_none=True,
190+
methods=["DELETE"],
191+
endpoint=self.client._jobs.delete_job,
192+
)
193+
194+
def register_get_estimate(self):
195+
"""Register Endpoint for Jobs (GET /jobs/{job_id}).
196+
197+
Returns:
198+
None
199+
"""
200+
self.router.add_api_route(
201+
name="get_estimate",
202+
path=f"/{self.client.settings.OPENEO_VERSION}/jobs" + "/{job_id}/estimate",
203+
response_model=responses.JobsGetEstimateGetResponse,
204+
response_model_exclude_unset=False,
205+
response_model_exclude_none=True,
206+
methods=["GET"],
207+
endpoint=self.client._jobs.estimate,
208+
)
209+
210+
def register_get_logs(self):
211+
"""Register Endpoint for Jobs (GET /jobs/{job_id}).
212+
213+
Returns:
214+
None
215+
"""
216+
self.router.add_api_route(
217+
name="get_logs",
218+
path=f"/{self.client.settings.OPENEO_VERSION}/jobs" + "/{job_id}/logs",
219+
response_model=responses.JobsGetLogsResponse,
220+
response_model_exclude_unset=False,
221+
response_model_exclude_none=True,
222+
methods=["GET"],
223+
endpoint=self.client._jobs.logs,
224+
)
225+
226+
def register_get_results(self):
227+
"""Register Endpoint for Jobs (GET /jobs/{job_id}).
228+
229+
Returns:
230+
None
231+
"""
232+
self.router.add_api_route(
233+
name="get_results",
234+
path=f"/{self.client.settings.OPENEO_VERSION}/jobs" + "/{job_id}/results",
235+
response_model=responses.Collection,
236+
response_model_exclude_unset=False,
237+
response_model_exclude_none=True,
238+
methods=["GET"],
239+
endpoint=self.client._jobs.get_results,
240+
)
241+
242+
def register_start_job(self):
243+
"""Register Endpoint for Jobs (GET /jobs/{job_id}).
244+
245+
Returns:
246+
None
247+
"""
248+
self.router.add_api_route(
249+
name="start_job",
250+
path=f"/{self.client.settings.OPENEO_VERSION}/jobs" + "/{job_id}/results",
251+
response_model=None,
252+
response_model_exclude_unset=False,
253+
response_model_exclude_none=True,
254+
methods=["POST"],
255+
endpoint=self.client._jobs.start_job,
256+
)
257+
258+
def register_cancel_job(self):
259+
"""Register Endpoint for Jobs (GET /jobs/{job_id}).
260+
261+
Returns:
262+
None
263+
"""
264+
self.router.add_api_route(
265+
name="start_job",
266+
path=f"/{self.client.settings.OPENEO_VERSION}/jobs" + "/{job_id}/results",
267+
response_model=None,
268+
response_model_exclude_unset=False,
269+
response_model_exclude_none=True,
270+
methods=["DELETE"],
271+
endpoint=self.client._jobs.cancel_job,
113272
)
114273

115274
def register_core(self):
@@ -132,6 +291,16 @@ def register_core(self):
132291
self.register_get_collections()
133292
self.register_get_collection()
134293
self.register_get_processes()
294+
self.register_get_jobs()
295+
self.register_create_job()
296+
self.register_update_job()
297+
self.register_get_job()
298+
self.register_delete_job()
299+
self.register_get_estimate()
300+
self.register_get_logs()
301+
self.register_get_results()
302+
self.register_start_job()
303+
self.register_cancel_job()
135304
self.register_well_known()
136305

137306
def http_exception_handler(self, request, exception):

openeo_fastapi/api/requests.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from typing import Optional
2+
3+
from pydantic import BaseModel, Extra, Field
4+
5+
from openeo_fastapi.api.types import Process
6+
7+
8+
class JobProcessGraph(Process):
9+
"""Model for some incoming requests to the api."""
10+
11+
process_graph_id: str = Field(default=None, alias="id")
12+
summary: Optional[str] = None
13+
description: Optional[str] = None
14+
parameters: Optional[list] = None
15+
returns: Optional[dict] = None
16+
process_graph: dict = None
17+
18+
class Config:
19+
allow_population_by_field_name = True
20+
extra = Extra.ignore
21+
22+
23+
class JobsRequest(BaseModel):
24+
"""Request model for job endpoints."""
25+
26+
title: str = None
27+
description: Optional[str] = None
28+
process: Optional[JobProcessGraph] = None
29+
plan: Optional[str] = None
30+
budget: Optional[str] = None

0 commit comments

Comments
 (0)