Skip to content

Commit 54417f9

Browse files
omkar-fossPaulKobow7536
authored andcommitted
AIP-84 Migrate the public endpoint Get DAG to FastAPI (apache#42848)
* Migrate the public endpoint Get DAG to FastAPI * Use proper name for test function
1 parent 7384b90 commit 54417f9

File tree

10 files changed

+313
-39
lines changed

10 files changed

+313
-39
lines changed

airflow/api_fastapi/openapi/v1-generated.yaml

+56-6
Original file line numberDiff line numberDiff line change
@@ -291,13 +291,13 @@ paths:
291291
application/json:
292292
schema:
293293
$ref: '#/components/schemas/HTTPValidationError'
294-
/public/dags/{dag_id}/details:
294+
/public/dags/{dag_id}:
295295
get:
296296
tags:
297297
- DAG
298-
summary: Get Dag Details
299-
description: Get details of DAG.
300-
operationId: get_dag_details
298+
summary: Get Dag
299+
description: Get basic information about a DAG.
300+
operationId: get_dag
301301
parameters:
302302
- name: dag_id
303303
in: path
@@ -311,7 +311,7 @@ paths:
311311
content:
312312
application/json:
313313
schema:
314-
$ref: '#/components/schemas/DAGDetailsResponse'
314+
$ref: '#/components/schemas/DAGResponse'
315315
'400':
316316
content:
317317
application/json:
@@ -342,7 +342,6 @@ paths:
342342
schema:
343343
$ref: '#/components/schemas/HTTPExceptionResponse'
344344
description: Unprocessable Entity
345-
/public/dags/{dag_id}:
346345
patch:
347346
tags:
348347
- DAG
@@ -409,6 +408,57 @@ paths:
409408
application/json:
410409
schema:
411410
$ref: '#/components/schemas/HTTPValidationError'
411+
/public/dags/{dag_id}/details:
412+
get:
413+
tags:
414+
- DAG
415+
summary: Get Dag Details
416+
description: Get details of DAG.
417+
operationId: get_dag_details
418+
parameters:
419+
- name: dag_id
420+
in: path
421+
required: true
422+
schema:
423+
type: string
424+
title: Dag Id
425+
responses:
426+
'200':
427+
description: Successful Response
428+
content:
429+
application/json:
430+
schema:
431+
$ref: '#/components/schemas/DAGDetailsResponse'
432+
'400':
433+
content:
434+
application/json:
435+
schema:
436+
$ref: '#/components/schemas/HTTPExceptionResponse'
437+
description: Bad Request
438+
'401':
439+
content:
440+
application/json:
441+
schema:
442+
$ref: '#/components/schemas/HTTPExceptionResponse'
443+
description: Unauthorized
444+
'403':
445+
content:
446+
application/json:
447+
schema:
448+
$ref: '#/components/schemas/HTTPExceptionResponse'
449+
description: Forbidden
450+
'404':
451+
content:
452+
application/json:
453+
schema:
454+
$ref: '#/components/schemas/HTTPExceptionResponse'
455+
description: Not Found
456+
'422':
457+
content:
458+
application/json:
459+
schema:
460+
$ref: '#/components/schemas/HTTPExceptionResponse'
461+
description: Unprocessable Entity
412462
/public/connections/{connection_id}:
413463
delete:
414464
tags:

airflow/api_fastapi/serializers/dags.py

+23-17
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
from itsdangerous import URLSafeSerializer
2525
from pendulum.tz.timezone import FixedTimezone, Timezone
2626
from pydantic import (
27-
AliasChoices,
27+
AliasGenerator,
2828
BaseModel,
29-
Field,
29+
ConfigDict,
3030
computed_field,
3131
field_validator,
3232
)
@@ -77,6 +77,14 @@ def get_owners(cls, v: Any) -> list[str] | None:
7777
return v.split(",")
7878
return v
7979

80+
@field_validator("timetable_summary", mode="before")
81+
@classmethod
82+
def get_timetable_summary(cls, tts: str | None) -> str | None:
83+
"""Validate the string representation of timetable_summary."""
84+
if tts is None or tts == "None":
85+
return None
86+
return str(tts)
87+
8088
# Mypy issue https://github.com/python/mypy/issues/1362
8189
@computed_field # type: ignore[misc]
8290
@property
@@ -103,9 +111,7 @@ class DAGDetailsResponse(DAGResponse):
103111
"""Specific serializer for DAG Details responses."""
104112

105113
catchup: bool
106-
dag_run_timeout: timedelta | None = Field(
107-
validation_alias=AliasChoices("dag_run_timeout", "dagrun_timeout")
108-
)
114+
dag_run_timeout: timedelta | None
109115
dataset_expression: dict | None
110116
doc_md: str | None
111117
start_date: datetime | None
@@ -114,11 +120,19 @@ class DAGDetailsResponse(DAGResponse):
114120
orientation: str
115121
params: abc.MutableMapping | None
116122
render_template_as_native_obj: bool
117-
template_search_path: Iterable[str] | None = Field(
118-
validation_alias=AliasChoices("template_search_path", "template_searchpath")
119-
)
123+
template_search_path: Iterable[str] | None
120124
timezone: str | None
121-
last_parsed: datetime | None = Field(validation_alias=AliasChoices("last_parsed", "last_loaded"))
125+
last_parsed: datetime | None
126+
127+
model_config = ConfigDict(
128+
alias_generator=AliasGenerator(
129+
validation_alias=lambda field_name: {
130+
"dag_run_timeout": "dagrun_timeout",
131+
"last_parsed": "last_loaded",
132+
"template_search_path": "template_searchpath",
133+
}.get(field_name, field_name),
134+
)
135+
)
122136

123137
@field_validator("timezone", mode="before")
124138
@classmethod
@@ -128,14 +142,6 @@ def get_timezone(cls, tz: Timezone | FixedTimezone) -> str | None:
128142
return None
129143
return str(tz)
130144

131-
@field_validator("timetable_summary", mode="before")
132-
@classmethod
133-
def get_timetable_summary(cls, tts: str | None) -> str | None:
134-
"""Validate the string representation of timetable_summary."""
135-
if tts is None or tts == "None":
136-
return None
137-
return str(tts)
138-
139145
@field_validator("params", mode="before")
140146
@classmethod
141147
def get_params(cls, params: abc.MutableMapping | None) -> dict | None:

airflow/api_fastapi/views/public/dags.py

+20
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,26 @@ async def get_dags(
9292
)
9393

9494

95+
@dags_router.get("/{dag_id}", responses=create_openapi_http_exception_doc([400, 401, 403, 404, 422]))
96+
async def get_dag(
97+
dag_id: str, session: Annotated[Session, Depends(get_session)], request: Request
98+
) -> DAGResponse:
99+
"""Get basic information about a DAG."""
100+
dag: DAG = request.app.state.dag_bag.get_dag(dag_id)
101+
if not dag:
102+
raise HTTPException(404, f"Dag with id {dag_id} was not found")
103+
104+
dag_model: DagModel = session.get(DagModel, dag_id)
105+
if not dag_model:
106+
raise HTTPException(404, f"Unable to obtain dag with id {dag_id} from session")
107+
108+
for key, value in dag.__dict__.items():
109+
if not key.startswith("_") and not hasattr(dag_model, key):
110+
setattr(dag_model, key, value)
111+
112+
return DAGResponse.model_validate(dag_model, from_attributes=True)
113+
114+
95115
@dags_router.get("/{dag_id}/details", responses=create_openapi_http_exception_doc([400, 401, 403, 404, 422]))
96116
async def get_dag_details(
97117
dag_id: str, session: Annotated[Session, Depends(get_session)], request: Request

airflow/ui/openapi-gen/queries/common.ts

+16
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,22 @@ export const UseDagServiceGetDagsKeyFn = (
9898
},
9999
]),
100100
];
101+
export type DagServiceGetDagDefaultResponse = Awaited<
102+
ReturnType<typeof DagService.getDag>
103+
>;
104+
export type DagServiceGetDagQueryResult<
105+
TData = DagServiceGetDagDefaultResponse,
106+
TError = unknown,
107+
> = UseQueryResult<TData, TError>;
108+
export const useDagServiceGetDagKey = "DagServiceGetDag";
109+
export const UseDagServiceGetDagKeyFn = (
110+
{
111+
dagId,
112+
}: {
113+
dagId: string;
114+
},
115+
queryKey?: Array<unknown>,
116+
) => [useDagServiceGetDagKey, ...(queryKey ?? [{ dagId }])];
101117
export type DagServiceGetDagDetailsDefaultResponse = Awaited<
102118
ReturnType<typeof DagService.getDagDetails>
103119
>;

airflow/ui/openapi-gen/queries/prefetch.ts

+20
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,26 @@ export const prefetchUseDagServiceGetDags = (
126126
tags,
127127
}),
128128
});
129+
/**
130+
* Get Dag
131+
* Get basic information about a DAG.
132+
* @param data The data for the request.
133+
* @param data.dagId
134+
* @returns DAGResponse Successful Response
135+
* @throws ApiError
136+
*/
137+
export const prefetchUseDagServiceGetDag = (
138+
queryClient: QueryClient,
139+
{
140+
dagId,
141+
}: {
142+
dagId: string;
143+
},
144+
) =>
145+
queryClient.prefetchQuery({
146+
queryKey: Common.UseDagServiceGetDagKeyFn({ dagId }),
147+
queryFn: () => DagService.getDag({ dagId }),
148+
});
129149
/**
130150
* Get Dag Details
131151
* Get details of DAG.

airflow/ui/openapi-gen/queries/queries.ts

+26
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,32 @@ export const useDagServiceGetDags = <
153153
}) as TData,
154154
...options,
155155
});
156+
/**
157+
* Get Dag
158+
* Get basic information about a DAG.
159+
* @param data The data for the request.
160+
* @param data.dagId
161+
* @returns DAGResponse Successful Response
162+
* @throws ApiError
163+
*/
164+
export const useDagServiceGetDag = <
165+
TData = Common.DagServiceGetDagDefaultResponse,
166+
TError = unknown,
167+
TQueryKey extends Array<unknown> = unknown[],
168+
>(
169+
{
170+
dagId,
171+
}: {
172+
dagId: string;
173+
},
174+
queryKey?: TQueryKey,
175+
options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
176+
) =>
177+
useQuery<TData, TError>({
178+
queryKey: Common.UseDagServiceGetDagKeyFn({ dagId }, queryKey),
179+
queryFn: () => DagService.getDag({ dagId }) as TData,
180+
...options,
181+
});
156182
/**
157183
* Get Dag Details
158184
* Get details of DAG.

airflow/ui/openapi-gen/queries/suspense.ts

+26
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,32 @@ export const useDagServiceGetDagsSuspense = <
148148
}) as TData,
149149
...options,
150150
});
151+
/**
152+
* Get Dag
153+
* Get basic information about a DAG.
154+
* @param data The data for the request.
155+
* @param data.dagId
156+
* @returns DAGResponse Successful Response
157+
* @throws ApiError
158+
*/
159+
export const useDagServiceGetDagSuspense = <
160+
TData = Common.DagServiceGetDagDefaultResponse,
161+
TError = unknown,
162+
TQueryKey extends Array<unknown> = unknown[],
163+
>(
164+
{
165+
dagId,
166+
}: {
167+
dagId: string;
168+
},
169+
queryKey?: TQueryKey,
170+
options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
171+
) =>
172+
useSuspenseQuery<TData, TError>({
173+
queryKey: Common.UseDagServiceGetDagKeyFn({ dagId }, queryKey),
174+
queryFn: () => DagService.getDag({ dagId }) as TData,
175+
...options,
176+
});
151177
/**
152178
* Get Dag Details
153179
* Get details of DAG.

airflow/ui/openapi-gen/requests/services.gen.ts

+36-9
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ import type {
1111
GetDagsResponse,
1212
PatchDagsData,
1313
PatchDagsResponse,
14-
GetDagDetailsData,
15-
GetDagDetailsResponse,
14+
GetDagData,
15+
GetDagResponse,
1616
PatchDagData,
1717
PatchDagResponse,
18+
GetDagDetailsData,
19+
GetDagDetailsResponse,
1820
DeleteConnectionData,
1921
DeleteConnectionResponse,
2022
GetConnectionData,
@@ -166,19 +168,17 @@ export class DagService {
166168
}
167169

168170
/**
169-
* Get Dag Details
170-
* Get details of DAG.
171+
* Get Dag
172+
* Get basic information about a DAG.
171173
* @param data The data for the request.
172174
* @param data.dagId
173-
* @returns DAGDetailsResponse Successful Response
175+
* @returns DAGResponse Successful Response
174176
* @throws ApiError
175177
*/
176-
public static getDagDetails(
177-
data: GetDagDetailsData,
178-
): CancelablePromise<GetDagDetailsResponse> {
178+
public static getDag(data: GetDagData): CancelablePromise<GetDagResponse> {
179179
return __request(OpenAPI, {
180180
method: "GET",
181-
url: "/public/dags/{dag_id}/details",
181+
url: "/public/dags/{dag_id}",
182182
path: {
183183
dag_id: data.dagId,
184184
},
@@ -225,6 +225,33 @@ export class DagService {
225225
},
226226
});
227227
}
228+
229+
/**
230+
* Get Dag Details
231+
* Get details of DAG.
232+
* @param data The data for the request.
233+
* @param data.dagId
234+
* @returns DAGDetailsResponse Successful Response
235+
* @throws ApiError
236+
*/
237+
public static getDagDetails(
238+
data: GetDagDetailsData,
239+
): CancelablePromise<GetDagDetailsResponse> {
240+
return __request(OpenAPI, {
241+
method: "GET",
242+
url: "/public/dags/{dag_id}/details",
243+
path: {
244+
dag_id: data.dagId,
245+
},
246+
errors: {
247+
400: "Bad Request",
248+
401: "Unauthorized",
249+
403: "Forbidden",
250+
404: "Not Found",
251+
422: "Unprocessable Entity",
252+
},
253+
});
254+
}
228255
}
229256

230257
export class ConnectionService {

0 commit comments

Comments
 (0)