This is an automated email from the ASF dual-hosted git repository. bbovenzi pushed a commit to branch mapped-instance-actions in repository https://gitbox.apache.org/repos/asf/airflow.git
commit 85f44b97dcc3dd46188127d73b4b3f01d08afb4c Author: Brent Bovenzi <[email protected]> AuthorDate: Sat Apr 9 13:27:38 2022 -0400 Add Xcom button, hide map index actions, disabled run --- airflow/www/static/js/dag.js | 18 +++++++++++++++++ .../content/taskInstance/MappedInstances.jsx | 17 +++++++++------- .../js/tree/details/content/taskInstance/Nav.jsx | 9 ++++++++- .../content/taskInstance/taskActions/Run.jsx | 21 ++++++++++++++++---- airflow/www/templates/airflow/dag.html | 23 ++++++++++++++-------- 5 files changed, 68 insertions(+), 20 deletions(-) diff --git a/airflow/www/static/js/dag.js b/airflow/www/static/js/dag.js index 1606737017..145d1f2666 100644 --- a/airflow/www/static/js/dag.js +++ b/airflow/www/static/js/dag.js @@ -124,6 +124,13 @@ function updateModalUrls() { execution_date: executionDate, map_index: mapIndex, }); + + updateButtonUrl(buttons.xcom, { + dag_id: dagId, + task_id: taskId, + execution_date: executionDate, + map_index: mapIndex, + }); } // Update modal urls on toggle @@ -170,9 +177,16 @@ export function callModal({ if (mi >= 0) { $('#modal_map_index').show(); $('#modal_map_index .value').text(mi); + // Marking state and clear are not yet supported for mapped instances + $('#success_action').hide(); + $('#failed_action').hide(); + $('#clear_action').hide(); } else { $('#modal_map_index').hide(); $('#modal_map_index .value').text(''); + $('#success_action').show(); + $('#failed_action').show(); + $('#clear_action').show(); } if (isSubDag) { $('#div_btn_subdag').show(); @@ -197,8 +211,12 @@ export function callModal({ $('#btn_mapped').show(); $('#mapped_dropdown').css('display', 'inline-block'); $('#btn_rendered').hide(); + $('#btn_xcom').hide(); + $('#btn_log').hide(); } else { $('#btn_rendered').show(); + $('#btn_xcom').show(); + $('#btn_log').show(); $('#btn_mapped').hide(); $('#mapped_dropdown').hide(); } diff --git a/airflow/www/static/js/tree/details/content/taskInstance/MappedInstances.jsx b/airflow/www/static/js/tree/details/content/taskInstance/MappedInstances.jsx index b815f0987f..42bbdca66f 100644 --- a/airflow/www/static/js/tree/details/content/taskInstance/MappedInstances.jsx +++ b/airflow/www/static/js/tree/details/content/taskInstance/MappedInstances.jsx @@ -26,9 +26,9 @@ import { IconButton, } from '@chakra-ui/react'; import { snakeCase } from 'lodash'; -import { FaMicroscope } from 'react-icons/fa'; -import { GiLog } from 'react-icons/gi'; -import { HiTemplate } from 'react-icons/hi'; +import { + MdDetails, MdCode, MdSyncAlt, MdReorder, +} from 'react-icons/md'; import { getMetaValue } from '../../../../utils'; import { formatDateTime, formatDuration } from '../../../../datetime_utils'; @@ -39,9 +39,10 @@ import Table from '../../../Table'; const renderedTemplatesUrl = getMetaValue('rendered_templates_url'); const logUrl = getMetaValue('log_url'); const taskUrl = getMetaValue('task_url'); +const xcomUrl = getMetaValue('xcom_url'); const IconLink = (props) => ( - <IconButton as={Link} variant="outline" colorScheme="blue" {...props} /> + <IconButton as={Link} variant="ghost" colorScheme="blue" fontSize="3xl" {...props} /> ); const MappedInstances = ({ @@ -73,6 +74,7 @@ const MappedInstances = ({ const detailsLink = `${taskUrl}&${params}`; const renderedLink = `${renderedTemplatesUrl}&${params}`; const logLink = `${logUrl}&${params}`; + const xcomLink = `${xcomUrl}&${params}`; return { ...mi, state: ( @@ -86,9 +88,10 @@ const MappedInstances = ({ endDate: mi.endDate && formatDateTime(mi.endDate), links: ( <Flex alignItems="center"> - <IconLink mr={1} title="Rendered Templates" aria-label="Rendered Templates" icon={<HiTemplate />} href={renderedLink} /> - <IconLink mr={1} title="Log" aria-label="Log" icon={<GiLog />} href={logLink} /> - <IconLink title="Details" aria-label="Details" icon={<FaMicroscope />} href={detailsLink} /> + <IconLink mr={1} title="Details" aria-label="Details" icon={<MdDetails />} href={detailsLink} /> + <IconLink mr={1} title="Rendered Templates" aria-label="Rendered Templates" icon={<MdCode />} href={renderedLink} /> + <IconLink mr={1} title="Log" aria-label="Log" icon={<MdReorder />} href={logLink} /> + <IconLink title="XCom" fontWeight="bold" aria-label="XCom" icon={<MdSyncAlt />} href={xcomLink} /> </Flex> ), }; diff --git a/airflow/www/static/js/tree/details/content/taskInstance/Nav.jsx b/airflow/www/static/js/tree/details/content/taskInstance/Nav.jsx index 7550bdb842..1b7062f898 100644 --- a/airflow/www/static/js/tree/details/content/taskInstance/Nav.jsx +++ b/airflow/www/static/js/tree/details/content/taskInstance/Nav.jsx @@ -34,12 +34,17 @@ const baseDate = getMetaValue('base_date'); const taskInstancesUrl = getMetaValue('task_instances_list_url'); const renderedK8sUrl = getMetaValue('rendered_k8s_url'); const renderedTemplatesUrl = getMetaValue('rendered_templates_url'); +const xcomUrl = getMetaValue('xcom_url'); const logUrl = getMetaValue('log_url'); const taskUrl = getMetaValue('task_url'); const gridUrl = getMetaValue('grid_url'); const gridUrlNoRoot = getMetaValue('grid_url_no_root'); -const LinkButton = ({ children, ...rest }) => (<Button as={Link} variant="ghost" colorScheme="blue" {...rest}>{children}</Button>); +const LinkButton = ({ children, ...rest }) => ( + <Button as={Link} aria-label={children} variant="ghost" colorScheme="blue" {...rest}> + {children} + </Button> +); const Nav = ({ taskId, executionDate, operator, isMapped, @@ -51,6 +56,7 @@ const Nav = ({ const detailsLink = `${taskUrl}&${params}`; const renderedLink = `${renderedTemplatesUrl}&${params}`; const logLink = `${logUrl}&${params}`; + const xcomLink = `${xcomUrl}&${params}`; const k8sLink = `${renderedK8sUrl}&${params}`; const listParams = new URLSearchParams({ _flt_3_dag_id: dagId, @@ -89,6 +95,7 @@ const Nav = ({ <LinkButton href={subDagLink}>Zoom into SubDag</LinkButton> )} <LinkButton href={logLink}>Log</LinkButton> + <LinkButton href={xcomLink}>XCom</LinkButton> </> )} <LinkButton href={allInstancesLink} title="View all instances across all DAG runs">All Instances</LinkButton> diff --git a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Run.jsx b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Run.jsx index 204cec44c2..d77c3e947d 100644 --- a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Run.jsx +++ b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Run.jsx @@ -22,15 +22,21 @@ import { Button, Flex, ButtonGroup, + Tooltip, } from '@chakra-ui/react'; import { useRunTask } from '../../../../api'; +import { getMetaValue } from '../../../../../utils'; +import { useContainerRef } from '../../../../context/containerRef'; + +const canRun = getMetaValue('k8s_or_k8scelery_executor') === 'True'; const Run = ({ dagId, runId, taskId, }) => { + const containerRef = useContainerRef(); const [ignoreAllDeps, setIgnoreAllDeps] = useState(false); const onToggleAllDeps = () => setIgnoreAllDeps(!ignoreAllDeps); @@ -52,7 +58,7 @@ const Run = ({ return ( <Flex justifyContent="space-between" width="100%"> - <ButtonGroup isAttached variant="outline"> + <ButtonGroup isAttached variant="outline" disabled={!canRun}> <Button bg={ignoreAllDeps && 'gray.100'} onClick={onToggleAllDeps} @@ -75,9 +81,16 @@ const Run = ({ Ignore Task Deps </Button> </ButtonGroup> - <Button colorScheme="blue" onClick={onClick} isLoading={isLoading}> - Run - </Button> + <Tooltip + label="Only works with the Celery, CeleryKubernetes or Kubernetes executors" + shouldWrapChildren // Will show the tooltip even if the button is disabled + disabled={canRun} + portalProps={{ containerRef }} + > + <Button colorScheme="blue" onClick={onClick} isLoading={isLoading} disabled={!canRun}> + Run + </Button> + </Tooltip> </Flex> ); }; diff --git a/airflow/www/templates/airflow/dag.html b/airflow/www/templates/airflow/dag.html index 16c0d27cb5..277cd53979 100644 --- a/airflow/www/templates/airflow/dag.html +++ b/airflow/www/templates/airflow/dag.html @@ -62,6 +62,7 @@ <meta name="dagrun_details_url" content="{{ url_for('Airflow.dagrun_details', redirect_url=request.base_url, dag_id=dag.dag_id) }}"> <meta name="task_url" content="{{ url_for('Airflow.task', dag_id=dag.dag_id) }}"> <meta name="log_url" content="{{ url_for('Airflow.log', dag_id=dag.dag_id) }}"> + <meta name="xcom_url" content="{{ url_for('Airflow.xcom', dag_id=dag.dag_id) }}"> <meta name="rendered_templates_url" content="{{ url_for('Airflow.rendered_templates', dag_id=dag.dag_id) }}"> <meta name="rendered_k8s_url" content="{{ url_for('Airflow.rendered_k8s', dag_id=dag.dag_id) }}"> <meta name="task_instances_list_url" content="{{ url_for('TaskInstanceModelView.list') }}"> @@ -234,6 +235,9 @@ <a id="btn_log" class="btn btn-sm" data-base-url="{{ url_for('Airflow.log') }}"> Log </a> + <a id="btn_xcom" class="btn btn-sm" data-base-url="{{ url_for('Airflow.xcom') }}"> + XCom + </a> <a id="btn_ti" class="btn btn-sm" data-base-url="{{ url_for('TaskInstanceModelView.list') }}" title="View all instances across all DAG runs"> All Instances </a> @@ -273,7 +277,7 @@ </div> {% endif %} <h4>Task Actions</h4> - <form method="POST" data-action="{{ url_for('Airflow.run') }}"> + <form method="POST" data-action="{{ url_for('Airflow.run') }}" id="run_action"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="dag_id" value="{{ dag.dag_id }}"> <input type="hidden" name="task_id"> @@ -281,6 +285,9 @@ <input type="hidden" name="map_index"> <input type="hidden" name="origin" value="{{ request.base_url }}"> <div class="row"> + <span class="col-xs-12 col-sm-9 text-danger" style="font-size: 12px"> + {{ "Only works with the Celery, CeleryKubernetes or Kubernetes executors, sorry" if not k8s_or_k8scelery_executor else "" }} + </span> <span class="btn-group col-xs-12 col-sm-9 task-instance-modal-column" data-toggle="buttons"> <label class="btn btn-default" @@ -299,14 +306,14 @@ </label> </span> <span class="col-xs-12 col-sm-3 task-instance-modal-column"> - <button type="submit" id="btn_run" class="btn btn-primary btn-block" title="Runs a single task instance"> + <button type="submit" id="btn_run" class="btn btn-primary btn-block" title="Runs a single task instance" {{ " disabled" if not k8s_or_k8scelery_executor else "" }}> Run </button> </span> </div> + <hr style="margin-bottom: 8px;"> </form> - <hr style="margin-bottom: 8px;"> - <form method="POST" data-action="{{ url_for('Airflow.clear') }}"> + <form method="POST" data-action="{{ url_for('Airflow.clear') }}" id="clear_action"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="dag_id" value="{{ dag.dag_id }}"> <input type="hidden" name="task_id"> @@ -346,9 +353,9 @@ </button> </span> </div> + <hr style="margin-bottom: 8px;"> </form> - <hr style="margin-bottom: 8px;"> - <form method="GET" data-action="{{ url_for('Airflow.confirm') }}"> + <form method="GET" data-action="{{ url_for('Airflow.confirm') }}" id="failed_action"> <input type="hidden" name="dag_id" value="{{ dag.dag_id }}"> <input type="hidden" name="task_id"> <input type="hidden" name="dag_run_id"> @@ -379,9 +386,9 @@ </button> </span> </div> + <hr style="margin-bottom: 8px;"> </form> - <hr style="margin-bottom: 8px;"> - <form method="GET" data-action="{{ url_for('Airflow.confirm') }}"> + <form method="GET" data-action="{{ url_for('Airflow.confirm') }}" id="success_action"> <input type="hidden" name="dag_id" value="{{ dag.dag_id }}"> <input type="hidden" name="task_id"> <input type="hidden" name="dag_run_id">
