prefect-client 3.2.1__py3-none-any.whl → 3.2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prefect/__init__.py +15 -8
- prefect/_build_info.py +5 -0
- prefect/_internal/schemas/bases.py +4 -7
- prefect/_internal/schemas/validators.py +5 -6
- prefect/_result_records.py +6 -1
- prefect/client/orchestration/__init__.py +18 -6
- prefect/client/schemas/schedules.py +2 -2
- prefect/concurrency/asyncio.py +4 -3
- prefect/concurrency/sync.py +3 -3
- prefect/concurrency/v1/asyncio.py +3 -3
- prefect/concurrency/v1/sync.py +3 -3
- prefect/deployments/flow_runs.py +2 -2
- prefect/docker/docker_image.py +2 -3
- prefect/engine.py +1 -1
- prefect/events/clients.py +4 -3
- prefect/events/related.py +3 -5
- prefect/flows.py +11 -5
- prefect/locking/filesystem.py +8 -8
- prefect/logging/handlers.py +7 -11
- prefect/main.py +0 -2
- prefect/runtime/flow_run.py +10 -17
- prefect/server/api/__init__.py +34 -0
- prefect/server/api/admin.py +85 -0
- prefect/server/api/artifacts.py +224 -0
- prefect/server/api/automations.py +239 -0
- prefect/server/api/block_capabilities.py +25 -0
- prefect/server/api/block_documents.py +164 -0
- prefect/server/api/block_schemas.py +153 -0
- prefect/server/api/block_types.py +211 -0
- prefect/server/api/clients.py +246 -0
- prefect/server/api/collections.py +75 -0
- prefect/server/api/concurrency_limits.py +286 -0
- prefect/server/api/concurrency_limits_v2.py +269 -0
- prefect/server/api/csrf_token.py +38 -0
- prefect/server/api/dependencies.py +196 -0
- prefect/server/api/deployments.py +941 -0
- prefect/server/api/events.py +300 -0
- prefect/server/api/flow_run_notification_policies.py +120 -0
- prefect/server/api/flow_run_states.py +52 -0
- prefect/server/api/flow_runs.py +867 -0
- prefect/server/api/flows.py +210 -0
- prefect/server/api/logs.py +43 -0
- prefect/server/api/middleware.py +73 -0
- prefect/server/api/root.py +35 -0
- prefect/server/api/run_history.py +170 -0
- prefect/server/api/saved_searches.py +99 -0
- prefect/server/api/server.py +891 -0
- prefect/server/api/task_run_states.py +52 -0
- prefect/server/api/task_runs.py +342 -0
- prefect/server/api/task_workers.py +31 -0
- prefect/server/api/templates.py +35 -0
- prefect/server/api/ui/__init__.py +3 -0
- prefect/server/api/ui/flow_runs.py +128 -0
- prefect/server/api/ui/flows.py +173 -0
- prefect/server/api/ui/schemas.py +63 -0
- prefect/server/api/ui/task_runs.py +175 -0
- prefect/server/api/validation.py +382 -0
- prefect/server/api/variables.py +181 -0
- prefect/server/api/work_queues.py +230 -0
- prefect/server/api/workers.py +656 -0
- prefect/settings/sources.py +18 -5
- prefect/states.py +3 -3
- prefect/task_engine.py +3 -3
- prefect/types/_datetime.py +82 -3
- prefect/utilities/dockerutils.py +2 -2
- prefect/workers/base.py +5 -5
- {prefect_client-3.2.1.dist-info → prefect_client-3.2.3.dist-info}/METADATA +10 -15
- {prefect_client-3.2.1.dist-info → prefect_client-3.2.3.dist-info}/RECORD +70 -32
- {prefect_client-3.2.1.dist-info → prefect_client-3.2.3.dist-info}/WHEEL +1 -2
- prefect/_version.py +0 -21
- prefect_client-3.2.1.dist-info/top_level.txt +0 -1
- {prefect_client-3.2.1.dist-info → prefect_client-3.2.3.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,300 @@
|
|
1
|
+
import base64
|
2
|
+
from typing import TYPE_CHECKING, List, Optional
|
3
|
+
|
4
|
+
from fastapi import Response, WebSocket, status
|
5
|
+
from fastapi.exceptions import HTTPException
|
6
|
+
from fastapi.param_functions import Depends, Path
|
7
|
+
from fastapi.params import Body, Query
|
8
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
9
|
+
from starlette.requests import Request
|
10
|
+
from starlette.status import WS_1002_PROTOCOL_ERROR
|
11
|
+
|
12
|
+
from prefect.logging import get_logger
|
13
|
+
from prefect.server.api.dependencies import is_ephemeral_request
|
14
|
+
from prefect.server.database import PrefectDBInterface, provide_database_interface
|
15
|
+
from prefect.server.events import messaging, stream
|
16
|
+
from prefect.server.events.counting import (
|
17
|
+
Countable,
|
18
|
+
InvalidEventCountParameters,
|
19
|
+
TimeUnit,
|
20
|
+
)
|
21
|
+
from prefect.server.events.filters import EventFilter, EventOrder
|
22
|
+
from prefect.server.events.models.automations import automations_session
|
23
|
+
from prefect.server.events.pipeline import EventsPipeline
|
24
|
+
from prefect.server.events.schemas.events import Event, EventCount, EventPage
|
25
|
+
from prefect.server.events.storage import (
|
26
|
+
INTERACTIVE_PAGE_SIZE,
|
27
|
+
InvalidTokenError,
|
28
|
+
database,
|
29
|
+
)
|
30
|
+
from prefect.server.utilities import subscriptions
|
31
|
+
from prefect.server.utilities.server import PrefectRouter
|
32
|
+
from prefect.settings import (
|
33
|
+
PREFECT_EVENTS_MAXIMUM_WEBSOCKET_BACKFILL,
|
34
|
+
PREFECT_EVENTS_WEBSOCKET_BACKFILL_PAGE_SIZE,
|
35
|
+
)
|
36
|
+
|
37
|
+
if TYPE_CHECKING:
|
38
|
+
import logging
|
39
|
+
|
40
|
+
logger: "logging.Logger" = get_logger(__name__)
|
41
|
+
|
42
|
+
|
43
|
+
router: PrefectRouter = PrefectRouter(prefix="/events", tags=["Events"])
|
44
|
+
|
45
|
+
|
46
|
+
@router.post("", status_code=status.HTTP_204_NO_CONTENT, response_class=Response)
|
47
|
+
async def create_events(
|
48
|
+
events: List[Event],
|
49
|
+
ephemeral_request: bool = Depends(is_ephemeral_request),
|
50
|
+
) -> None:
|
51
|
+
"""Record a batch of Events"""
|
52
|
+
if ephemeral_request:
|
53
|
+
await EventsPipeline().process_events(events)
|
54
|
+
else:
|
55
|
+
received_events = [event.receive() for event in events]
|
56
|
+
await messaging.publish(received_events)
|
57
|
+
|
58
|
+
|
59
|
+
@router.websocket("/in")
|
60
|
+
async def stream_events_in(websocket: WebSocket) -> None:
|
61
|
+
"""Open a WebSocket to stream incoming Events"""
|
62
|
+
|
63
|
+
await websocket.accept()
|
64
|
+
|
65
|
+
try:
|
66
|
+
async with messaging.create_event_publisher() as publisher:
|
67
|
+
async for event_json in websocket.iter_text():
|
68
|
+
event = Event.model_validate_json(event_json)
|
69
|
+
await publisher.publish_event(event.receive())
|
70
|
+
except subscriptions.NORMAL_DISCONNECT_EXCEPTIONS: # pragma: no cover
|
71
|
+
pass # it's fine if a client disconnects either normally or abnormally
|
72
|
+
|
73
|
+
return None
|
74
|
+
|
75
|
+
|
76
|
+
@router.websocket("/out")
|
77
|
+
async def stream_workspace_events_out(
|
78
|
+
websocket: WebSocket,
|
79
|
+
) -> None:
|
80
|
+
"""Open a WebSocket to stream Events"""
|
81
|
+
websocket = await subscriptions.accept_prefect_socket(
|
82
|
+
websocket,
|
83
|
+
)
|
84
|
+
if not websocket:
|
85
|
+
return
|
86
|
+
|
87
|
+
try:
|
88
|
+
# After authentication, the next message is expected to be a filter message, any
|
89
|
+
# other type of message will close the connection.
|
90
|
+
message = await websocket.receive_json()
|
91
|
+
|
92
|
+
if message["type"] != "filter":
|
93
|
+
return await websocket.close(
|
94
|
+
WS_1002_PROTOCOL_ERROR, reason="Expected 'filter' message"
|
95
|
+
)
|
96
|
+
|
97
|
+
wants_backfill = message.get("backfill", True)
|
98
|
+
|
99
|
+
try:
|
100
|
+
filter = EventFilter.model_validate(message["filter"])
|
101
|
+
except Exception as e:
|
102
|
+
return await websocket.close(
|
103
|
+
WS_1002_PROTOCOL_ERROR, reason=f"Invalid filter: {e}"
|
104
|
+
)
|
105
|
+
|
106
|
+
filter.occurred.clamp(PREFECT_EVENTS_MAXIMUM_WEBSOCKET_BACKFILL.value())
|
107
|
+
filter.order = EventOrder.ASC
|
108
|
+
|
109
|
+
# subscribe to the ongoing event stream first so we don't miss events...
|
110
|
+
async with stream.events(filter) as event_stream:
|
111
|
+
# ...then if the user wants, backfill up to the last 1k events...
|
112
|
+
if wants_backfill:
|
113
|
+
backfilled_ids = set()
|
114
|
+
|
115
|
+
async with automations_session() as session:
|
116
|
+
backfill, _, next_page = await database.query_events(
|
117
|
+
session=session,
|
118
|
+
filter=filter,
|
119
|
+
page_size=PREFECT_EVENTS_WEBSOCKET_BACKFILL_PAGE_SIZE.value(),
|
120
|
+
)
|
121
|
+
|
122
|
+
while backfill:
|
123
|
+
for event in backfill:
|
124
|
+
backfilled_ids.add(event.id)
|
125
|
+
await websocket.send_json(
|
126
|
+
{
|
127
|
+
"type": "event",
|
128
|
+
"event": event.model_dump(mode="json"),
|
129
|
+
}
|
130
|
+
)
|
131
|
+
|
132
|
+
if not next_page:
|
133
|
+
break
|
134
|
+
|
135
|
+
backfill, _, next_page = await database.query_next_page(
|
136
|
+
session=session,
|
137
|
+
page_token=next_page,
|
138
|
+
)
|
139
|
+
|
140
|
+
# ...before resuming the ongoing stream of events
|
141
|
+
async for event in event_stream:
|
142
|
+
if not event:
|
143
|
+
if await subscriptions.still_connected(websocket):
|
144
|
+
continue
|
145
|
+
break
|
146
|
+
|
147
|
+
if wants_backfill and event.id in backfilled_ids:
|
148
|
+
backfilled_ids.remove(event.id)
|
149
|
+
continue
|
150
|
+
|
151
|
+
await websocket.send_json(
|
152
|
+
{"type": "event", "event": event.model_dump(mode="json")}
|
153
|
+
)
|
154
|
+
|
155
|
+
except subscriptions.NORMAL_DISCONNECT_EXCEPTIONS: # pragma: no cover
|
156
|
+
pass # it's fine if a client disconnects either normally or abnormally
|
157
|
+
|
158
|
+
return None
|
159
|
+
|
160
|
+
|
161
|
+
def verified_page_token(
|
162
|
+
page_token: str = Query(..., alias="page-token"),
|
163
|
+
) -> str:
|
164
|
+
try:
|
165
|
+
page_token = base64.b64decode(page_token.encode()).decode()
|
166
|
+
except Exception:
|
167
|
+
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
168
|
+
|
169
|
+
if not page_token:
|
170
|
+
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
171
|
+
|
172
|
+
return page_token
|
173
|
+
|
174
|
+
|
175
|
+
@router.post(
|
176
|
+
"/filter",
|
177
|
+
)
|
178
|
+
async def read_events(
|
179
|
+
request: Request,
|
180
|
+
filter: Optional[EventFilter] = Body(
|
181
|
+
None,
|
182
|
+
description=(
|
183
|
+
"Additional optional filter criteria to narrow down the set of Events"
|
184
|
+
),
|
185
|
+
),
|
186
|
+
limit: int = Body(
|
187
|
+
INTERACTIVE_PAGE_SIZE,
|
188
|
+
ge=0,
|
189
|
+
le=INTERACTIVE_PAGE_SIZE,
|
190
|
+
embed=True,
|
191
|
+
description="The number of events to return with each page",
|
192
|
+
),
|
193
|
+
db: PrefectDBInterface = Depends(provide_database_interface),
|
194
|
+
) -> EventPage:
|
195
|
+
"""
|
196
|
+
Queries for Events matching the given filter criteria in the given Account. Returns
|
197
|
+
the first page of results, and the URL to request the next page (if there are more
|
198
|
+
results).
|
199
|
+
"""
|
200
|
+
filter = filter or EventFilter()
|
201
|
+
async with db.session_context() as session:
|
202
|
+
events, total, next_token = await database.query_events(
|
203
|
+
session=session,
|
204
|
+
filter=filter,
|
205
|
+
page_size=limit,
|
206
|
+
)
|
207
|
+
|
208
|
+
return EventPage(
|
209
|
+
events=events,
|
210
|
+
total=total,
|
211
|
+
next_page=generate_next_page_link(request, next_token),
|
212
|
+
)
|
213
|
+
|
214
|
+
|
215
|
+
@router.get(
|
216
|
+
"/filter/next",
|
217
|
+
)
|
218
|
+
async def read_account_events_page(
|
219
|
+
request: Request,
|
220
|
+
page_token: str = Depends(verified_page_token),
|
221
|
+
db: PrefectDBInterface = Depends(provide_database_interface),
|
222
|
+
) -> EventPage:
|
223
|
+
"""
|
224
|
+
Returns the next page of Events for a previous query against the given Account, and
|
225
|
+
the URL to request the next page (if there are more results).
|
226
|
+
"""
|
227
|
+
async with db.session_context() as session:
|
228
|
+
try:
|
229
|
+
events, total, next_token = await database.query_next_page(
|
230
|
+
session=session, page_token=page_token
|
231
|
+
)
|
232
|
+
except InvalidTokenError:
|
233
|
+
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
234
|
+
|
235
|
+
return EventPage(
|
236
|
+
events=events,
|
237
|
+
total=total,
|
238
|
+
next_page=generate_next_page_link(request, next_token),
|
239
|
+
)
|
240
|
+
|
241
|
+
|
242
|
+
def generate_next_page_link(
|
243
|
+
request: Request,
|
244
|
+
page_token: Optional[str],
|
245
|
+
) -> Optional[str]:
|
246
|
+
if not page_token:
|
247
|
+
return None
|
248
|
+
|
249
|
+
next_page = (
|
250
|
+
f"{request.base_url}api/events/filter/next"
|
251
|
+
f"?page-token={base64.b64encode(page_token.encode()).decode()}"
|
252
|
+
)
|
253
|
+
return next_page
|
254
|
+
|
255
|
+
|
256
|
+
@router.post(
|
257
|
+
"/count-by/{countable}",
|
258
|
+
)
|
259
|
+
async def count_account_events(
|
260
|
+
filter: EventFilter,
|
261
|
+
countable: Countable = Path(...),
|
262
|
+
time_unit: TimeUnit = Body(default=TimeUnit.day),
|
263
|
+
time_interval: float = Body(default=1.0, ge=0.01),
|
264
|
+
db: PrefectDBInterface = Depends(provide_database_interface),
|
265
|
+
) -> List[EventCount]:
|
266
|
+
"""
|
267
|
+
Returns distinct objects and the count of events associated with them. Objects
|
268
|
+
that can be counted include the day the event occurred, the type of event, or
|
269
|
+
the IDs of the resources associated with the event.
|
270
|
+
"""
|
271
|
+
async with db.session_context() as session:
|
272
|
+
return await handle_event_count_request(
|
273
|
+
session=session,
|
274
|
+
filter=filter,
|
275
|
+
countable=countable,
|
276
|
+
time_unit=time_unit,
|
277
|
+
time_interval=time_interval,
|
278
|
+
)
|
279
|
+
|
280
|
+
|
281
|
+
async def handle_event_count_request(
|
282
|
+
session: AsyncSession,
|
283
|
+
filter: EventFilter,
|
284
|
+
countable: Countable,
|
285
|
+
time_unit: TimeUnit,
|
286
|
+
time_interval: float,
|
287
|
+
) -> List[EventCount]:
|
288
|
+
try:
|
289
|
+
return await database.count_events(
|
290
|
+
session=session,
|
291
|
+
filter=filter,
|
292
|
+
countable=countable,
|
293
|
+
time_unit=time_unit,
|
294
|
+
time_interval=time_interval,
|
295
|
+
)
|
296
|
+
except InvalidEventCountParameters as exc:
|
297
|
+
raise HTTPException(
|
298
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
299
|
+
detail=exc.message,
|
300
|
+
)
|
@@ -0,0 +1,120 @@
|
|
1
|
+
"""
|
2
|
+
Routes for interacting with flow run notification policy objects.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import List
|
6
|
+
from uuid import UUID
|
7
|
+
|
8
|
+
from fastapi import Body, Depends, HTTPException, Path, status
|
9
|
+
|
10
|
+
import prefect.server.api.dependencies as dependencies
|
11
|
+
import prefect.server.models as models
|
12
|
+
import prefect.server.schemas as schemas
|
13
|
+
from prefect.server.database import PrefectDBInterface, provide_database_interface
|
14
|
+
from prefect.server.utilities.server import PrefectRouter
|
15
|
+
|
16
|
+
router: PrefectRouter = PrefectRouter(
|
17
|
+
prefix="/flow_run_notification_policies", tags=["Flow Run Notification Policies"]
|
18
|
+
)
|
19
|
+
|
20
|
+
|
21
|
+
@router.post("/", status_code=status.HTTP_201_CREATED)
|
22
|
+
async def create_flow_run_notification_policy(
|
23
|
+
flow_run_notification_policy: schemas.actions.FlowRunNotificationPolicyCreate,
|
24
|
+
db: PrefectDBInterface = Depends(provide_database_interface),
|
25
|
+
) -> schemas.core.FlowRunNotificationPolicy:
|
26
|
+
"""
|
27
|
+
Creates a new flow run notification policy.
|
28
|
+
"""
|
29
|
+
async with db.session_context(begin_transaction=True) as session:
|
30
|
+
return await models.flow_run_notification_policies.create_flow_run_notification_policy(
|
31
|
+
session=session, flow_run_notification_policy=flow_run_notification_policy
|
32
|
+
)
|
33
|
+
|
34
|
+
|
35
|
+
@router.patch("/{id}", status_code=status.HTTP_204_NO_CONTENT)
|
36
|
+
async def update_flow_run_notification_policy(
|
37
|
+
flow_run_notification_policy: schemas.actions.FlowRunNotificationPolicyUpdate,
|
38
|
+
flow_run_notification_policy_id: UUID = Path(
|
39
|
+
..., description="The flow run notification policy id", alias="id"
|
40
|
+
),
|
41
|
+
db: PrefectDBInterface = Depends(provide_database_interface),
|
42
|
+
) -> None:
|
43
|
+
"""
|
44
|
+
Updates an existing flow run notification policy.
|
45
|
+
"""
|
46
|
+
async with db.session_context(begin_transaction=True) as session:
|
47
|
+
result = await models.flow_run_notification_policies.update_flow_run_notification_policy(
|
48
|
+
session=session,
|
49
|
+
flow_run_notification_policy_id=flow_run_notification_policy_id,
|
50
|
+
flow_run_notification_policy=flow_run_notification_policy,
|
51
|
+
)
|
52
|
+
if not result:
|
53
|
+
raise HTTPException(
|
54
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
55
|
+
detail=f"Flow run notification policy {id} not found",
|
56
|
+
)
|
57
|
+
|
58
|
+
|
59
|
+
@router.get("/{id}")
|
60
|
+
async def read_flow_run_notification_policy(
|
61
|
+
flow_run_notification_policy_id: UUID = Path(
|
62
|
+
..., description="The flow run notification policy id", alias="id"
|
63
|
+
),
|
64
|
+
db: PrefectDBInterface = Depends(provide_database_interface),
|
65
|
+
) -> schemas.core.FlowRunNotificationPolicy:
|
66
|
+
"""
|
67
|
+
Get a flow run notification policy by id.
|
68
|
+
"""
|
69
|
+
async with db.session_context() as session:
|
70
|
+
flow_run_notification_policy = await models.flow_run_notification_policies.read_flow_run_notification_policy(
|
71
|
+
session=session,
|
72
|
+
flow_run_notification_policy_id=flow_run_notification_policy_id,
|
73
|
+
)
|
74
|
+
if not flow_run_notification_policy:
|
75
|
+
raise HTTPException(
|
76
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
77
|
+
detail="flow run notification policy not found",
|
78
|
+
)
|
79
|
+
return flow_run_notification_policy
|
80
|
+
|
81
|
+
|
82
|
+
@router.post("/filter")
|
83
|
+
async def read_flow_run_notification_policies(
|
84
|
+
limit: int = dependencies.LimitBody(),
|
85
|
+
flow_run_notification_policy_filter: schemas.filters.FlowRunNotificationPolicyFilter = None,
|
86
|
+
offset: int = Body(0, ge=0),
|
87
|
+
db: PrefectDBInterface = Depends(provide_database_interface),
|
88
|
+
) -> List[schemas.core.FlowRunNotificationPolicy]:
|
89
|
+
"""
|
90
|
+
Query for flow run notification policies.
|
91
|
+
"""
|
92
|
+
async with db.session_context() as session:
|
93
|
+
return await models.flow_run_notification_policies.read_flow_run_notification_policies(
|
94
|
+
session=session,
|
95
|
+
flow_run_notification_policy_filter=flow_run_notification_policy_filter,
|
96
|
+
offset=offset,
|
97
|
+
limit=limit,
|
98
|
+
)
|
99
|
+
|
100
|
+
|
101
|
+
@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT)
|
102
|
+
async def delete_flow_run_notification_policy(
|
103
|
+
flow_run_notification_policy_id: UUID = Path(
|
104
|
+
..., description="The flow run notification policy id", alias="id"
|
105
|
+
),
|
106
|
+
db: PrefectDBInterface = Depends(provide_database_interface),
|
107
|
+
) -> None:
|
108
|
+
"""
|
109
|
+
Delete a flow run notification policy by id.
|
110
|
+
"""
|
111
|
+
async with db.session_context(begin_transaction=True) as session:
|
112
|
+
result = await models.flow_run_notification_policies.delete_flow_run_notification_policy(
|
113
|
+
session=session,
|
114
|
+
flow_run_notification_policy_id=flow_run_notification_policy_id,
|
115
|
+
)
|
116
|
+
if not result:
|
117
|
+
raise HTTPException(
|
118
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
119
|
+
detail="flow run notification policy not found",
|
120
|
+
)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
"""
|
2
|
+
Routes for interacting with flow run state objects.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import List
|
6
|
+
from uuid import UUID
|
7
|
+
|
8
|
+
from fastapi import Depends, HTTPException, Path, status
|
9
|
+
|
10
|
+
import prefect.server.models as models
|
11
|
+
import prefect.server.schemas as schemas
|
12
|
+
from prefect.server.database import PrefectDBInterface, provide_database_interface
|
13
|
+
from prefect.server.utilities.server import PrefectRouter
|
14
|
+
|
15
|
+
router: PrefectRouter = PrefectRouter(
|
16
|
+
prefix="/flow_run_states", tags=["Flow Run States"]
|
17
|
+
)
|
18
|
+
|
19
|
+
|
20
|
+
@router.get("/{id}")
|
21
|
+
async def read_flow_run_state(
|
22
|
+
flow_run_state_id: UUID = Path(
|
23
|
+
..., description="The flow run state id", alias="id"
|
24
|
+
),
|
25
|
+
db: PrefectDBInterface = Depends(provide_database_interface),
|
26
|
+
) -> schemas.states.State:
|
27
|
+
"""
|
28
|
+
Get a flow run state by id.
|
29
|
+
"""
|
30
|
+
async with db.session_context() as session:
|
31
|
+
flow_run_state = await models.flow_run_states.read_flow_run_state(
|
32
|
+
session=session, flow_run_state_id=flow_run_state_id
|
33
|
+
)
|
34
|
+
if not flow_run_state:
|
35
|
+
raise HTTPException(
|
36
|
+
status.HTTP_404_NOT_FOUND, detail="Flow run state not found"
|
37
|
+
)
|
38
|
+
return flow_run_state
|
39
|
+
|
40
|
+
|
41
|
+
@router.get("/")
|
42
|
+
async def read_flow_run_states(
|
43
|
+
flow_run_id: UUID,
|
44
|
+
db: PrefectDBInterface = Depends(provide_database_interface),
|
45
|
+
) -> List[schemas.states.State]:
|
46
|
+
"""
|
47
|
+
Get states associated with a flow run.
|
48
|
+
"""
|
49
|
+
async with db.session_context() as session:
|
50
|
+
return await models.flow_run_states.read_flow_run_states(
|
51
|
+
session=session, flow_run_id=flow_run_id
|
52
|
+
)
|