Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ class GridRunsResponse(BaseModel):
run_after: datetime
state: DagRunState | None
run_type: DagRunType
bundle_version: str | None = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is bundle_version used?

dag_version_number: int | None = None

@computed_field
def duration(self) -> float:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class LightGridTaskInstanceSummary(BaseModel):
child_states: dict[TaskInstanceState | None, int] | None
min_start_date: datetime | None
max_end_date: datetime | None
dag_version_number: int | None = None


class GridTISummaries(BaseModel):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2095,6 +2095,16 @@ components:
- type: 'null'
run_type:
$ref: '#/components/schemas/DagRunType'
bundle_version:
anyOf:
- type: string
- type: 'null'
title: Bundle Version
dag_version_number:
anyOf:
- type: integer
- type: 'null'
title: Dag Version Number
duration:
type: number
title: Duration
Expand Down Expand Up @@ -2361,6 +2371,11 @@ components:
format: date-time
- type: 'null'
title: Max End Date
dag_version_number:
anyOf:
- type: integer
- type: 'null'
title: Dag Version Number
type: object
required:
- task_id
Expand Down
44 changes: 32 additions & 12 deletions airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

import structlog
from fastapi import Depends, HTTPException, status
from sqlalchemy import select
from sqlalchemy import func, select
from sqlalchemy.orm import joinedload

from airflow.api_fastapi.auth.managers.models.resource_details import DagAccessEntity
Expand Down Expand Up @@ -274,17 +274,34 @@ def get_grid_runs(
triggering_user: QueryDagRunTriggeringUserSearch,
) -> list[GridRunsResponse]:
"""Get info about a run for the grid."""
# Retrieve, sort the previous DAG Runs
base_query = select(
DagRun.dag_id,
DagRun.run_id,
DagRun.queued_at,
DagRun.start_date,
DagRun.end_date,
DagRun.run_after,
DagRun.state,
DagRun.run_type,
).where(DagRun.dag_id == dag_id)
# get the highest dag_version_number from TIs for each run
latest_ti_version = (
select(
TaskInstance.run_id,
func.max(DagVersion.version_number).label("version_number"),
)
.join(DagVersion, TaskInstance.dag_version_id == DagVersion.id)
.where(TaskInstance.dag_id == dag_id)
.group_by(TaskInstance.run_id)
.subquery()
)
Comment on lines +277 to +287
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to go through all that trouble to only retrieve the 'last' version. Just return the DagRun.dag_versions attribute.

Also this will be consistent for other contributor. People will get confused as to why a DagRun has dag_versions as a list in the plublic interface, but on the grid endpoint here it's only dag_version and not a list anymore.

I find this confusing and it adds a lot of unecessary application code.


base_query = (
select(
DagRun.dag_id,
DagRun.run_id,
DagRun.queued_at,
DagRun.start_date,
DagRun.end_date,
DagRun.run_after,
DagRun.state,
DagRun.run_type,
DagRun.bundle_version,
latest_ti_version.c.version_number.label("dag_version_number"),
)
.outerjoin(latest_ti_version, DagRun.run_id == latest_ti_version.c.run_id)
.where(DagRun.dag_id == dag_id)
)

# This comparison is to fall back to DAG timetable when no order_by is provided
if order_by.value == [order_by.get_primary_key_string()]:
Expand Down Expand Up @@ -355,7 +372,9 @@ def get_grid_ti_summaries(
TaskInstance.dag_version_id,
TaskInstance.start_date,
TaskInstance.end_date,
DagVersion.version_number,
)
.outerjoin(DagVersion, TaskInstance.dag_version_id == DagVersion.id)
.where(TaskInstance.dag_id == dag_id)
.where(
TaskInstance.run_id == run_id,
Expand All @@ -378,6 +397,7 @@ def get_grid_ti_summaries(
"state": ti.state,
"start_date": ti.start_date,
"end_date": ti.end_date,
"dag_version_number": ti.version_number,
}
)
serdag = _get_serdag(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,18 @@ def _get_aggs_for_node(detail):
max_end_date = max(x["end_date"] for x in detail if x["end_date"])
except ValueError:
max_end_date = None

dag_version_numbers = [
x.get("dag_version_number") for x in detail if x.get("dag_version_number") is not None
]
dag_version_number = max(dag_version_numbers) if dag_version_numbers else None

return {
"state": agg_state(states),
"min_start_date": min_start_date,
"max_end_date": max_end_date,
"child_states": dict(Counter(states)),
"dag_version_number": dag_version_number,
}


Expand Down
33 changes: 33 additions & 0 deletions airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8021,6 +8021,28 @@ export const $GridRunsResponse = {
run_type: {
'$ref': '#/components/schemas/DagRunType'
},
bundle_version: {
anyOf: [
{
type: 'string'
},
{
type: 'null'
}
],
title: 'Bundle Version'
},
dag_version_number: {
anyOf: [
{
type: 'integer'
},
{
type: 'null'
}
],
title: 'Dag Version Number'
},
duration: {
type: 'number',
title: 'Duration',
Expand Down Expand Up @@ -8132,6 +8154,17 @@ export const $LightGridTaskInstanceSummary = {
}
],
title: 'Max End Date'
},
dag_version_number: {
anyOf: [
{
type: 'integer'
},
{
type: 'null'
}
],
title: 'Dag Version Number'
}
},
type: 'object',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1974,6 +1974,8 @@ export type GridRunsResponse = {
run_after: string;
state: DagRunState | null;
run_type: DagRunType;
bundle_version?: string | null;
dag_version_number?: number | null;
readonly duration: number;
};

Expand Down Expand Up @@ -2007,6 +2009,7 @@ export type LightGridTaskInstanceSummary = {
} | null;
min_start_date: string | null;
max_end_date: string | null;
dag_version_number?: number | null;
};

/**
Expand Down
9 changes: 9 additions & 0 deletions airflow-core/src/airflow/ui/public/i18n/locales/en/dag.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@
"graphDirection": {
"label": "Graph Direction"
},
"showVersionIndicator": {
"label": "Show Version Indicator",
"options": {
"hideAll": "Hide All",
"showAll": "Show All",
"showBundleVersion": "Show Bundle Version",
"showDagVersion": "Show Dag Version"
}
},
"taskStreamFilter": {
"activeFilter": "Active filter",
"clearFilter": "Clear Filter",
Expand Down
122 changes: 122 additions & 0 deletions airflow-core/src/airflow/ui/src/components/ui/VersionIndicator.tsx
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we only use this in the grid view then we should move it to layouts/Details/Grid instead. components are for general reshareable components. (/ui was also supposed to be for wrappers that weren't included in chakra-ui but that we hadn't customized, but we haven't been enforcing that too well)

Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { Box } from "@chakra-ui/react";
import { useTranslation } from "react-i18next";
import { FiGitCommit } from "react-icons/fi";

import { Tooltip } from "src/components/ui";

type BundleVersionIndicatorProps = {
readonly bundleVersion: string | undefined;
};

export const BundleVersionIndicator = ({ bundleVersion }: BundleVersionIndicatorProps) => {
const { t: translate } = useTranslation("components");

return (
<Tooltip content={`${translate("versionDetails.bundleVersion")}: ${bundleVersion}`}>
<Box color="orange.focusRing" left={-2} position="absolute" top={93} zIndex={1}>
<FiGitCommit size={15} />
</Box>
</Tooltip>
);
};

const CONTAINER_STYLES = {
horizontal: {
height: 0.5,
left: "50%",
top: 0,
transform: "translate(-50%, -50%)",
width: 4.5,
},
vertical: {
height: 104,
left: -1.25,
top: -1.5,
width: 0.5,
},
} as const;

const CIRCLE_STYLES = {
horizontal: {
height: 1.5,
left: "50%",
top: "50%",
transform: "translate(-50%, -50%)",
width: 1.5,
},
vertical: {
height: 1.5,
left: "50%",
top: -1,
transform: "translateX(-50%)",
width: 1.5,
},
} as const;

type DagVersionIndicatorProps = {
readonly dagVersionNumber: number | undefined;
readonly orientation?: "horizontal" | "vertical";
};

export const DagVersionIndicator = ({
dagVersionNumber,
orientation = "vertical",
}: DagVersionIndicatorProps) => {
const isVertical = orientation === "vertical";
const currentContainerStyle = CONTAINER_STYLES[orientation];
const currentCircleStyle = CIRCLE_STYLES[orientation];

return (
<Box
aria-label={`Version ${dagVersionNumber} indicator`}
as="output"
position="absolute"
zIndex={1}
{...currentContainerStyle}
>
<Box
bg="orange.focusRing"
height={isVertical ? "full" : 0.5}
position="absolute"
width={isVertical ? 0.5 : "full"}
/>

<Tooltip
content={`v${dagVersionNumber ?? ""}`}
positioning={{
placement: isVertical ? "top" : "right",
}}
>
<Box
_hover={{
cursor: "pointer",
transform: `${currentCircleStyle.transform} scale(1.2)`,
}}
bg="orange.focusRing"
borderRadius="full"
position="absolute"
transition="all 0.2s ease-in-out"
{...currentCircleStyle}
/>
</Tooltip>
</Box>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { createListCollection } from "@chakra-ui/react";

export enum VersionIndicatorDisplayOptions {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VersionIndicatorOptions or VersionDisplayOptions

We use this a lot. Let's try to have a simpler variable name.

ALL = "all",
BUNDLE = "bundle",
DAG = "dag",
Comment on lines +23 to +24
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

Suggested change
BUNDLE = "bundle",
DAG = "dag",
BUNDLE_VERSION = "bundle",
DAG_VERSION = "dag",

NONE = "none",
}

const validOptions = new Set<string>(Object.values(VersionIndicatorDisplayOptions));

export const isVersionIndicatorDisplayOption = (value: unknown): value is VersionIndicatorDisplayOptions =>
typeof value === "string" && validOptions.has(value);

export const showVersionIndicatorOptions = createListCollection({
items: [
{ label: "dag:panel.showVersionIndicator.options.showAll", value: VersionIndicatorDisplayOptions.ALL },
{
label: "dag:panel.showVersionIndicator.options.showBundleVersion",
value: VersionIndicatorDisplayOptions.BUNDLE,
},
{
label: "dag:panel.showVersionIndicator.options.showDagVersion",
value: VersionIndicatorDisplayOptions.DAG,
},
{ label: "dag:panel.showVersionIndicator.options.hideAll", value: VersionIndicatorDisplayOptions.NONE },
],
});
Loading
Loading