You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by bb...@apache.org on 2022/04/21 16:17:19 UTC

[airflow] branch fix-grid-errors updated (b84599b99c -> b7819154d6)

This is an automated email from the ASF dual-hosted git repository.

bbovenzi pushed a change to branch fix-grid-errors
in repository https://gitbox.apache.org/repos/asf/airflow.git


 discard b84599b99c Fix grid error handling
    omit b77aa9a408 Get gantt/graph modal actions working again
    omit 039b259d2a Chain map_index params
    omit 6716bd08dd Fix gantt/graph modal
    omit 19edb5ebb0 Readd mapped instance table selection
    omit 2075e5fb61 Allow bulk mapped task actions
     add 8dedd2ac13 Improve logging of optional provider features messages (#23037)
     add 79ab71efe7 Replace `DummyOperator` usage in test_zip.zip and test_zip_invalid_cron.zip (#23123)
     add 07a13bb708 Change ComputeSSH to throw provider import error instead paramiko (#23035)
     add 9fdac9aec4 Simplify Task exception trackback truncation to never warn (#23121)
     add af45483b95 Ensure that we don't schedule all mapped TIs when one is cleared (#23130)
     add 03f7d857e9 When expanding a task end up skipping it, ensure we don't deadlock the DagRun (#23134)
     add 03bef084b3 add script to initialise virtualenv (#22971)
     add 1e8ac47589 Task actions UI for individual mapped instances (#23127)
     add 91b82763c5 Fix TI failure handling when task cannot be unmapped. (#23119)
     add e57c7aeda8 Fix timezone display for logs on UI (#23075)
     add c7399c7190 KubernetesHook should try incluster first when not otherwise configured (#23126)
     new 2fef8d505c Allow bulk mapped task actions
     new 67be2dd5e4 Readd mapped instance table selection
     new 088929deac Fix gantt/graph modal
     new b7819154d6 Fix grid error handling

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (b84599b99c)
            \
             N -- N -- N   refs/heads/fix-grid-errors (b7819154d6)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 BREEZE.rst                                         |   4 +-
 CONTRIBUTING.rst                                   |  39 ++--
 CONTRIBUTORS_QUICK_START.rst                       |   4 +-
 airflow/dag_processing/processor.py                |   2 +-
 airflow/models/dagrun.py                           |  69 +++---
 airflow/models/mappedoperator.py                   |  25 +--
 airflow/models/taskfail.py                         |  14 +-
 airflow/models/taskinstance.py                     | 246 +++++++--------------
 .../providers/cncf/kubernetes/hooks/kubernetes.py  |  22 +-
 .../providers/google/cloud/hooks/compute_ssh.py    |   7 +-
 airflow/providers_manager.py                       |  91 +++++---
 airflow/www/static/js/dag.js                       |   3 +
 airflow/www/static/js/datetime_utils.js            |   2 +-
 airflow/www/static/js/ti_log.js                    |   5 +-
 dev/provider_packages/README.md                    |   6 +-
 .../installation/installing-from-pypi.rst          |   3 +
 scripts/tools/initialize_virtualenv.py             | 186 ++++++++++++++++
 tests/api/common/test_delete_dag.py                |  10 +-
 tests/callbacks/test_callback_requests.py          |   2 +-
 tests/core/test_providers_manager.py               |   5 +-
 tests/dags/test_zip.zip                            | Bin 3307 -> 3513 bytes
 tests/dags/test_zip_invalid_cron.zip               | Bin 913 -> 1513 bytes
 tests/jobs/test_scheduler_job.py                   |   5 +-
 tests/models/test_dagrun.py                        |  36 ++-
 .../cncf/kubernetes/hooks/test_kubernetes.py       |  33 ++-
 25 files changed, 519 insertions(+), 300 deletions(-)
 create mode 100755 scripts/tools/initialize_virtualenv.py


[airflow] 02/04: Readd mapped instance table selection

Posted by bb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

bbovenzi pushed a commit to branch fix-grid-errors
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 67be2dd5e4d694c0b2087d1847d1154479851614
Author: Brent Bovenzi <br...@gmail.com>
AuthorDate: Tue Apr 12 14:51:45 2022 -0400

    Readd mapped instance table selection
---
 airflow/www/static/js/tree/Table.jsx               |  4 ++--
 .../js/tree/details/content/taskInstance/index.jsx | 25 ++++++++++------------
 .../content/taskInstance/taskActions/Clear.jsx     |  5 +++--
 .../taskInstance/taskActions/MarkFailed.jsx        |  6 ++++--
 .../taskInstance/taskActions/MarkSuccess.jsx       | 10 +++++----
 .../content/taskInstance/taskActions/Run.jsx       | 24 ++++++---------------
 6 files changed, 33 insertions(+), 41 deletions(-)

diff --git a/airflow/www/static/js/tree/Table.jsx b/airflow/www/static/js/tree/Table.jsx
index 152f647ea3..e500b08966 100644
--- a/airflow/www/static/js/tree/Table.jsx
+++ b/airflow/www/static/js/tree/Table.jsx
@@ -46,7 +46,7 @@ import {
 } from 'react-icons/ti';
 
 const IndeterminateCheckbox = forwardRef(
-  ({ indeterminate, ...rest }, ref) => {
+  ({ indeterminate, checked, ...rest }, ref) => {
     const defaultRef = useRef();
     const resolvedRef = ref || defaultRef;
 
@@ -55,7 +55,7 @@ const IndeterminateCheckbox = forwardRef(
     }, [resolvedRef, indeterminate]);
 
     return (
-      <Checkbox ref={resolvedRef} {...rest} />
+      <Checkbox ref={resolvedRef} isChecked={checked} {...rest} />
     );
   },
 );
diff --git a/airflow/www/static/js/tree/details/content/taskInstance/index.jsx b/airflow/www/static/js/tree/details/content/taskInstance/index.jsx
index 62ffee156a..0e4f441e24 100644
--- a/airflow/www/static/js/tree/details/content/taskInstance/index.jsx
+++ b/airflow/www/static/js/tree/details/content/taskInstance/index.jsx
@@ -24,7 +24,6 @@ import {
   Divider,
   StackDivider,
   Text,
-  Flex,
 } from '@chakra-ui/react';
 
 import RunAction from './taskActions/Run';
@@ -89,39 +88,32 @@ const TaskInstance = ({ taskId, runId }) => {
       {!isGroup && (
         <Box my={3}>
           <Text as="strong">{taskActionsTitle}</Text>
-          <Flex maxHeight="20px" minHeight="20px">
-            {selectedRows.length ? (
-              <Text color="red.500">
-                Clear, Mark Failed, and Mark Success do not yet work with individual mapped tasks.
-              </Text>
-            ) : <Divider my={2} />}
-          </Flex>
-          {/* visibility={selectedRows.length ? 'visible' : 'hidden'} */}
+          <Divider my={2} />
           <VStack justifyContent="center" divider={<StackDivider my={3} />}>
             <RunAction
               runId={runId}
               taskId={taskId}
               dagId={dagId}
-              selectedRows={selectedRows}
+              mapIndexes={selectedRows}
             />
             <ClearAction
               runId={runId}
               taskId={taskId}
               dagId={dagId}
               executionDate={executionDate}
-              selectedRows={selectedRows}
+              mapIndexes={selectedRows}
             />
             <MarkFailedAction
               runId={runId}
               taskId={taskId}
               dagId={dagId}
-              selectedRows={selectedRows}
+              mapIndexes={selectedRows}
             />
             <MarkSuccessAction
               runId={runId}
               taskId={taskId}
               dagId={dagId}
-              selectedRows={selectedRows}
+              mapIndexes={selectedRows}
             />
           </VStack>
           <Divider my={2} />
@@ -143,7 +135,12 @@ const TaskInstance = ({ taskId, runId }) => {
         extraLinks={extraLinks}
       />
       {isMapped && (
-        <MappedInstances dagId={dagId} runId={runId} taskId={taskId} selectRows={setSelectedRows} />
+        <MappedInstances
+          dagId={dagId}
+          runId={runId}
+          taskId={taskId}
+          selectRows={setSelectedRows}
+        />
       )}
     </Box>
   );
diff --git a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Clear.jsx b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Clear.jsx
index cada7b59ed..d825976ed2 100644
--- a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Clear.jsx
+++ b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Clear.jsx
@@ -34,7 +34,7 @@ const Run = ({
   runId,
   taskId,
   executionDate,
-  selectedRows,
+  mapIndexes,
 }) => {
   const [affectedTasks, setAffectedTasks] = useState([]);
 
@@ -74,6 +74,7 @@ const Run = ({
         recursive,
         failed,
         confirmed: false,
+        mapIndexes,
       });
       setAffectedTasks(data);
       onOpen();
@@ -92,6 +93,7 @@ const Run = ({
         recursive,
         failed,
         confirmed: true,
+        mapIndexes,
       });
       setAffectedTasks([]);
       onClose();
@@ -114,7 +116,6 @@ const Run = ({
         colorScheme="blue"
         onClick={onClick}
         isLoading={isLoading}
-        isDisabled={!!selectedRows.length}
         title="Clearing deletes the previous state of the task instance, allowing it to get re-triggered by the scheduler or a backfill command"
       >
         Clear
diff --git a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkFailed.jsx b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkFailed.jsx
index 6bc10c066e..12f8bcfeef 100644
--- a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkFailed.jsx
+++ b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkFailed.jsx
@@ -33,7 +33,7 @@ const MarkFailed = ({
   dagId,
   runId,
   taskId,
-  selectedRows,
+  mapIndexes,
 }) => {
   const [affectedTasks, setAffectedTasks] = useState([]);
 
@@ -69,6 +69,7 @@ const MarkFailed = ({
         future,
         upstream,
         downstream,
+        mapIndexes,
       });
       setAffectedTasks(data);
       onOpen();
@@ -84,6 +85,7 @@ const MarkFailed = ({
         future,
         upstream,
         downstream,
+        mapIndexes,
       });
       setAffectedTasks([]);
       onClose();
@@ -100,7 +102,7 @@ const MarkFailed = ({
         <ActionButton bg={upstream && 'gray.100'} onClick={onToggleUpstream} name="Upstream" />
         <ActionButton bg={downstream && 'gray.100'} onClick={onToggleDownstream} name="Downstream" />
       </ButtonGroup>
-      <Button colorScheme="red" onClick={onClick} isLoading={isMarkLoading || isConfirmLoading} isDisabled={!!selectedRows.length}>
+      <Button colorScheme="red" onClick={onClick} isLoading={isMarkLoading || isConfirmLoading}>
         Mark Failed
       </Button>
       <ConfirmDialog
diff --git a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkSuccess.jsx b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkSuccess.jsx
index b4d2b8c047..bdf59e6a4d 100644
--- a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkSuccess.jsx
+++ b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkSuccess.jsx
@@ -29,8 +29,8 @@ import ConfirmDialog from '../../ConfirmDialog';
 import ActionButton from './ActionButton';
 import { useMarkSuccessTask, useConfirmMarkTask } from '../../../../api';
 
-const Run = ({
-  dagId, runId, taskId, selectedRows,
+const MarkSuccess = ({
+  dagId, runId, taskId, mapIndexes,
 }) => {
   const [affectedTasks, setAffectedTasks] = useState([]);
 
@@ -64,6 +64,7 @@ const Run = ({
         future,
         upstream,
         downstream,
+        mapIndexes,
       });
       setAffectedTasks(data);
       onOpen();
@@ -79,6 +80,7 @@ const Run = ({
         future,
         upstream,
         downstream,
+        mapIndexes,
       });
       setAffectedTasks([]);
       onClose();
@@ -95,7 +97,7 @@ const Run = ({
         <ActionButton bg={upstream && 'gray.100'} onClick={onToggleUpstream} name="Upstream" />
         <ActionButton bg={downstream && 'gray.100'} onClick={onToggleDownstream} name="Downstream" />
       </ButtonGroup>
-      <Button colorScheme="green" onClick={onClick} isLoading={isMarkLoading || isConfirmLoading} isDisabled={!!selectedRows.length}>
+      <Button colorScheme="green" onClick={onClick} isLoading={isMarkLoading || isConfirmLoading}>
         Mark Success
       </Button>
       <ConfirmDialog
@@ -109,4 +111,4 @@ const Run = ({
   );
 };
 
-export default Run;
+export default MarkSuccess;
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 41c8bb9c6f..85d502aeed 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
@@ -30,7 +30,7 @@ const Run = ({
   dagId,
   runId,
   taskId,
-  selectedRows,
+  mapIndexes,
 }) => {
   const [ignoreAllDeps, setIgnoreAllDeps] = useState(false);
   const onToggleAllDeps = () => setIgnoreAllDeps(!ignoreAllDeps);
@@ -44,22 +44,12 @@ const Run = ({
   const { mutate: onRun, isLoading } = useRunTask(dagId, runId, taskId);
 
   const onClick = () => {
-    if (selectedRows.length) {
-      selectedRows.forEach((mapIndex) => {
-        onRun({
-          ignoreAllDeps,
-          ignoreTaskState,
-          ignoreTaskDeps,
-          mapIndex,
-        });
-      });
-    } else {
-      onRun({
-        ignoreAllDeps,
-        ignoreTaskState,
-        ignoreTaskDeps,
-      });
-    }
+    onRun({
+      ignoreAllDeps,
+      ignoreTaskState,
+      ignoreTaskDeps,
+      mapIndexes,
+    });
   };
 
   return (


[airflow] 04/04: Fix grid error handling

Posted by bb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

bbovenzi pushed a commit to branch fix-grid-errors
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit b7819154d6d48ad4629114bd3a54fafcbb9870e9
Author: Brent Bovenzi <br...@gmail.com>
AuthorDate: Thu Apr 21 11:56:40 2022 -0400

    Fix grid error handling
---
 airflow/www/static/js/tree/api/useClearRun.js      | 23 +++------
 airflow/www/static/js/tree/api/useClearTask.js     | 23 +++------
 airflow/www/static/js/tree/api/useMarkFailedRun.js |  3 ++
 .../www/static/js/tree/api/useMarkFailedTask.js    |  3 ++
 .../www/static/js/tree/api/useMarkSuccessRun.js    |  3 ++
 .../www/static/js/tree/api/useMarkSuccessTask.js   |  3 ++
 airflow/www/static/js/tree/api/useQueueRun.js      |  3 ++
 airflow/www/static/js/tree/api/useRunTask.js       | 23 +++------
 airflow/www/static/js/tree/api/useTreeData.js      | 15 +++---
 .../js/tree/details/content/dagRun/ClearRun.jsx    | 20 +++-----
 .../tree/details/content/dagRun/MarkFailedRun.jsx  | 10 ++--
 .../tree/details/content/dagRun/MarkSuccessRun.jsx | 20 +++-----
 .../js/tree/details/content/dagRun/QueueRun.jsx    | 20 +++-----
 .../content/taskInstance/taskActions/Clear.jsx     | 56 ++++++++++------------
 .../taskInstance/taskActions/MarkFailed.jsx        | 44 +++++++----------
 .../taskInstance/taskActions/MarkSuccess.jsx       | 44 +++++++----------
 airflow/www/static/js/tree/useErrorToast.js        | 46 ++++++++++++++++++
 airflow/www/views.py                               | 24 +++++-----
 18 files changed, 182 insertions(+), 201 deletions(-)

diff --git a/airflow/www/static/js/tree/api/useClearRun.js b/airflow/www/static/js/tree/api/useClearRun.js
index 9a5c21a8aa..afcb620c3a 100644
--- a/airflow/www/static/js/tree/api/useClearRun.js
+++ b/airflow/www/static/js/tree/api/useClearRun.js
@@ -18,17 +18,17 @@
  */
 
 import axios from 'axios';
-import { useToast } from '@chakra-ui/react';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
+import useErrorToast from '../useErrorToast';
 
 const csrfToken = getMetaValue('csrf_token');
 const clearRunUrl = getMetaValue('dagrun_clear_url');
 
 export default function useClearRun(dagId, runId) {
   const queryClient = useQueryClient();
-  const toast = useToast();
+  const errorToast = useErrorToast();
   const { startRefresh } = useAutoRefresh();
   return useMutation(
     ['dagRunClear', dagId, runId],
@@ -47,21 +47,12 @@ export default function useClearRun(dagId, runId) {
       });
     },
     {
-      onSuccess: (data) => {
-        const { message, status } = data;
-        if (message && status === 'error') {
-          toast({
-            description: message,
-            isClosable: true,
-            status,
-          });
-        }
-        if (!status || status !== 'error') {
-          // Invalidating the query will force a new API request
-          queryClient.invalidateQueries('treeData');
-          startRefresh();
-        }
+      onSuccess: () => {
+        // Invalidating the query will force a new API request
+        queryClient.invalidateQueries('treeData');
+        startRefresh();
       },
+      onError: (error) => errorToast({ error }),
     },
   );
 }
diff --git a/airflow/www/static/js/tree/api/useClearTask.js b/airflow/www/static/js/tree/api/useClearTask.js
index 2ea3eee486..777a621aaf 100644
--- a/airflow/www/static/js/tree/api/useClearTask.js
+++ b/airflow/www/static/js/tree/api/useClearTask.js
@@ -18,10 +18,10 @@
  */
 
 import axios from 'axios';
-import { useToast } from '@chakra-ui/react';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
+import useErrorToast from '../useErrorToast';
 
 const csrfToken = getMetaValue('csrf_token');
 const clearUrl = getMetaValue('clear_url');
@@ -30,7 +30,7 @@ export default function useClearTask({
   dagId, runId, taskId, executionDate,
 }) {
   const queryClient = useQueryClient();
-  const toast = useToast();
+  const errorToast = useErrorToast();
   const { startRefresh } = useAutoRefresh();
 
   return useMutation(
@@ -64,21 +64,12 @@ export default function useClearTask({
       });
     },
     {
-      onSuccess: (data) => {
-        const { message, status } = data;
-        if (message && status === 'error') {
-          toast({
-            description: message,
-            isClosable: true,
-            status,
-          });
-        }
-        if (!status || status !== 'error') {
-          queryClient.invalidateQueries('treeData');
-          queryClient.invalidateQueries('mappedInstances', dagId, runId, taskId);
-          startRefresh();
-        }
+      onSuccess: () => {
+        queryClient.invalidateQueries('treeData');
+        queryClient.invalidateQueries('mappedInstances', dagId, runId, taskId);
+        startRefresh();
       },
+      onError: (error) => errorToast({ error }),
     },
   );
 }
diff --git a/airflow/www/static/js/tree/api/useMarkFailedRun.js b/airflow/www/static/js/tree/api/useMarkFailedRun.js
index 725d9907db..c7487be5e9 100644
--- a/airflow/www/static/js/tree/api/useMarkFailedRun.js
+++ b/airflow/www/static/js/tree/api/useMarkFailedRun.js
@@ -21,12 +21,14 @@ import axios from 'axios';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
+import useErrorToast from '../useErrorToast';
 
 const csrfToken = getMetaValue('csrf_token');
 const markFailedUrl = getMetaValue('dagrun_failed_url');
 
 export default function useMarkFailedRun(dagId, runId) {
   const queryClient = useQueryClient();
+  const errorToast = useErrorToast();
   const { startRefresh } = useAutoRefresh();
   return useMutation(
     ['dagRunFailed', dagId, runId],
@@ -49,6 +51,7 @@ export default function useMarkFailedRun(dagId, runId) {
         queryClient.invalidateQueries('treeData');
         startRefresh();
       },
+      onError: (error) => errorToast({ error }),
     },
   );
 }
diff --git a/airflow/www/static/js/tree/api/useMarkFailedTask.js b/airflow/www/static/js/tree/api/useMarkFailedTask.js
index a94ab22d0c..4ef72df1ba 100644
--- a/airflow/www/static/js/tree/api/useMarkFailedTask.js
+++ b/airflow/www/static/js/tree/api/useMarkFailedTask.js
@@ -21,6 +21,7 @@ import axios from 'axios';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
+import useErrorToast from '../useErrorToast';
 
 const failedUrl = getMetaValue('failed_url');
 const csrfToken = getMetaValue('csrf_token');
@@ -29,6 +30,7 @@ export default function useMarkFailedTask({
   dagId, runId, taskId,
 }) {
   const queryClient = useQueryClient();
+  const errorToast = useErrorToast();
   const { startRefresh } = useAutoRefresh();
   return useMutation(
     ['markFailed', dagId, runId, taskId],
@@ -63,6 +65,7 @@ export default function useMarkFailedTask({
         queryClient.invalidateQueries('mappedInstances', dagId, runId, taskId);
         startRefresh();
       },
+      onError: (error) => errorToast({ error }),
     },
   );
 }
diff --git a/airflow/www/static/js/tree/api/useMarkSuccessRun.js b/airflow/www/static/js/tree/api/useMarkSuccessRun.js
index 9f769c997c..6076e6ba1e 100644
--- a/airflow/www/static/js/tree/api/useMarkSuccessRun.js
+++ b/airflow/www/static/js/tree/api/useMarkSuccessRun.js
@@ -21,12 +21,14 @@ import axios from 'axios';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
+import useErrorToast from '../useErrorToast';
 
 const markSuccessUrl = getMetaValue('dagrun_success_url');
 const csrfToken = getMetaValue('csrf_token');
 
 export default function useMarkSuccessRun(dagId, runId) {
   const queryClient = useQueryClient();
+  const errorToast = useErrorToast();
   const { startRefresh } = useAutoRefresh();
   return useMutation(
     ['dagRunSuccess', dagId, runId],
@@ -49,6 +51,7 @@ export default function useMarkSuccessRun(dagId, runId) {
         queryClient.invalidateQueries('treeData');
         startRefresh();
       },
+      onError: (error) => errorToast({ error }),
     },
   );
 }
diff --git a/airflow/www/static/js/tree/api/useMarkSuccessTask.js b/airflow/www/static/js/tree/api/useMarkSuccessTask.js
index 47fda2f0f8..14d83e653d 100644
--- a/airflow/www/static/js/tree/api/useMarkSuccessTask.js
+++ b/airflow/www/static/js/tree/api/useMarkSuccessTask.js
@@ -21,6 +21,7 @@ import axios from 'axios';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
+import useErrorToast from '../useErrorToast';
 
 const csrfToken = getMetaValue('csrf_token');
 const successUrl = getMetaValue('success_url');
@@ -29,6 +30,7 @@ export default function useMarkSuccessTask({
   dagId, runId, taskId,
 }) {
   const queryClient = useQueryClient();
+  const errorToast = useErrorToast();
   const { startRefresh } = useAutoRefresh();
   return useMutation(
     ['markSuccess', dagId, runId, taskId],
@@ -64,6 +66,7 @@ export default function useMarkSuccessTask({
         queryClient.invalidateQueries('mappedInstances', dagId, runId, taskId);
         startRefresh();
       },
+      onError: (error) => errorToast({ error }),
     },
   );
 }
diff --git a/airflow/www/static/js/tree/api/useQueueRun.js b/airflow/www/static/js/tree/api/useQueueRun.js
index 20612f1142..5848d680f9 100644
--- a/airflow/www/static/js/tree/api/useQueueRun.js
+++ b/airflow/www/static/js/tree/api/useQueueRun.js
@@ -21,12 +21,14 @@ import axios from 'axios';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
+import useErrorToast from '../useErrorToast';
 
 const csrfToken = getMetaValue('csrf_token');
 const queuedUrl = getMetaValue('dagrun_queued_url');
 
 export default function useQueueRun(dagId, runId) {
   const queryClient = useQueryClient();
+  const errorToast = useErrorToast();
   const { startRefresh } = useAutoRefresh();
   return useMutation(
     ['dagRunQueue', dagId, runId],
@@ -49,6 +51,7 @@ export default function useQueueRun(dagId, runId) {
         queryClient.invalidateQueries('treeData');
         startRefresh();
       },
+      onError: (error) => errorToast({ error }),
     },
   );
 }
diff --git a/airflow/www/static/js/tree/api/useRunTask.js b/airflow/www/static/js/tree/api/useRunTask.js
index 9e45c42f59..810b2dceab 100644
--- a/airflow/www/static/js/tree/api/useRunTask.js
+++ b/airflow/www/static/js/tree/api/useRunTask.js
@@ -18,17 +18,17 @@
  */
 
 import axios from 'axios';
-import { useToast } from '@chakra-ui/react';
 import { useMutation, useQueryClient } from 'react-query';
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
+import useErrorToast from '../useErrorToast';
 
 const csrfToken = getMetaValue('csrf_token');
 const runUrl = getMetaValue('run_url');
 
 export default function useRunTask(dagId, runId, taskId) {
   const queryClient = useQueryClient();
-  const toast = useToast();
+  const errorToast = useErrorToast();
   const { startRefresh } = useAutoRefresh();
   return useMutation(
     ['runTask', dagId, runId, taskId],
@@ -58,21 +58,12 @@ export default function useRunTask(dagId, runId, taskId) {
       }),
     ),
     {
-      onSuccess: (data) => {
-        const { message, status } = data.length ? data[0] : data;
-        if (message && status === 'error') {
-          toast({
-            description: message,
-            isClosable: true,
-            status,
-          });
-        }
-        if (!status || status !== 'error') {
-          queryClient.invalidateQueries('treeData');
-          queryClient.invalidateQueries('mappedInstances', dagId, runId, taskId);
-          startRefresh();
-        }
+      onSuccess: () => {
+        queryClient.invalidateQueries('treeData');
+        queryClient.invalidateQueries('mappedInstances', dagId, runId, taskId);
+        startRefresh();
       },
+      onError: (error) => errorToast({ error }),
     },
   );
 }
diff --git a/airflow/www/static/js/tree/api/useTreeData.js b/airflow/www/static/js/tree/api/useTreeData.js
index ddb0a9dfed..a33e17c1c4 100644
--- a/airflow/www/static/js/tree/api/useTreeData.js
+++ b/airflow/www/static/js/tree/api/useTreeData.js
@@ -21,11 +21,11 @@
 
 import { useQuery } from 'react-query';
 import axios from 'axios';
-import { useToast } from '@chakra-ui/react';
 
 import { getMetaValue } from '../../utils';
 import { useAutoRefresh } from '../context/autorefresh';
 import { formatData, areActiveRuns } from '../treeDataUtils';
+import useErrorToast from '../useErrorToast';
 
 // dagId comes from dag.html
 const dagId = getMetaValue('dag_id');
@@ -41,7 +41,7 @@ const useTreeData = () => {
   };
   const initialData = formatData(treeData, emptyData);
   const { isRefreshOn, stopRefresh } = useAutoRefresh();
-  const toast = useToast();
+  const errorToast = useErrorToast();
   return useQuery('treeData', async () => {
     try {
       const root = urlRoot ? `&root=${urlRoot}` : '';
@@ -50,16 +50,13 @@ const useTreeData = () => {
       // turn off auto refresh if there are no active runs
       if (!areActiveRuns(newData.dagRuns)) stopRefresh();
       return newData;
-    } catch (e) {
+    } catch (error) {
       stopRefresh();
-      // Display error in a toast message
-      toast({
+      errorToast({
         title: 'Auto-refresh Error',
-        description: e.message,
-        isClosable: true,
-        status: 'error',
+        error,
       });
-      throw (e);
+      throw (error);
     }
   }, {
     // only enabled and refetch if the refresh switch is on
diff --git a/airflow/www/static/js/tree/details/content/dagRun/ClearRun.jsx b/airflow/www/static/js/tree/details/content/dagRun/ClearRun.jsx
index bbd7ffba42..95aed65f8c 100644
--- a/airflow/www/static/js/tree/details/content/dagRun/ClearRun.jsx
+++ b/airflow/www/static/js/tree/details/content/dagRun/ClearRun.jsx
@@ -29,23 +29,15 @@ const ClearRun = ({ dagId, runId }) => {
   const { mutateAsync: onClear, isLoading } = useClearRun(dagId, runId);
 
   const onClick = async () => {
-    try {
-      const data = await onClear({ confirmed: false });
-      setAffectedTasks(data);
-      onOpen();
-    } catch (e) {
-      console.error(e);
-    }
+    const data = await onClear({ confirmed: false });
+    setAffectedTasks(data);
+    onOpen();
   };
 
   const onConfirm = async () => {
-    try {
-      await onClear({ confirmed: true });
-      setAffectedTasks([]);
-      onClose();
-    } catch (e) {
-      console.error(e);
-    }
+    await onClear({ confirmed: true });
+    setAffectedTasks([]);
+    onClose();
   };
 
   return (
diff --git a/airflow/www/static/js/tree/details/content/dagRun/MarkFailedRun.jsx b/airflow/www/static/js/tree/details/content/dagRun/MarkFailedRun.jsx
index 226643a77b..483dbd4fe0 100644
--- a/airflow/www/static/js/tree/details/content/dagRun/MarkFailedRun.jsx
+++ b/airflow/www/static/js/tree/details/content/dagRun/MarkFailedRun.jsx
@@ -29,13 +29,9 @@ const MarkFailedRun = ({ dagId, runId }) => {
   const { mutateAsync: markFailed, isLoading } = useMarkFailedRun(dagId, runId);
 
   const onClick = async () => {
-    try {
-      const data = await markFailed({ confirmed: false });
-      setAffectedTasks(data);
-      onOpen();
-    } catch (error) {
-      console.error(error);
-    }
+    const data = await markFailed({ confirmed: false });
+    setAffectedTasks(data);
+    onOpen();
   };
 
   const onConfirm = () => {
diff --git a/airflow/www/static/js/tree/details/content/dagRun/MarkSuccessRun.jsx b/airflow/www/static/js/tree/details/content/dagRun/MarkSuccessRun.jsx
index 30715c340b..0d1d8483d3 100644
--- a/airflow/www/static/js/tree/details/content/dagRun/MarkSuccessRun.jsx
+++ b/airflow/www/static/js/tree/details/content/dagRun/MarkSuccessRun.jsx
@@ -29,23 +29,15 @@ const MarkSuccessRun = ({ dagId, runId }) => {
   const { mutateAsync: markSuccess, isLoading } = useMarkSuccessRun(dagId, runId);
 
   const onClick = async () => {
-    try {
-      const data = await markSuccess({ confirmed: false });
-      setAffectedTasks(data);
-      onOpen();
-    } catch (e) {
-      console.error(e);
-    }
+    const data = await markSuccess({ confirmed: false });
+    setAffectedTasks(data);
+    onOpen();
   };
 
   const onConfirm = async () => {
-    try {
-      await markSuccess({ confirmed: true });
-      setAffectedTasks([]);
-      onClose();
-    } catch (e) {
-      console.error(e);
-    }
+    await markSuccess({ confirmed: true });
+    setAffectedTasks([]);
+    onClose();
   };
 
   return (
diff --git a/airflow/www/static/js/tree/details/content/dagRun/QueueRun.jsx b/airflow/www/static/js/tree/details/content/dagRun/QueueRun.jsx
index ef87d9fea5..ceec45ffdd 100644
--- a/airflow/www/static/js/tree/details/content/dagRun/QueueRun.jsx
+++ b/airflow/www/static/js/tree/details/content/dagRun/QueueRun.jsx
@@ -30,24 +30,16 @@ const QueueRun = ({ dagId, runId }) => {
 
   // Get what the changes will be and show it in a modal
   const onClick = async () => {
-    try {
-      const data = await onQueue({ confirmed: false });
-      setAffectedTasks(data);
-      onOpen();
-    } catch (e) {
-      console.error(e);
-    }
+    const data = await onQueue({ confirmed: false });
+    setAffectedTasks(data);
+    onOpen();
   };
 
   // Confirm changes
   const onConfirm = async () => {
-    try {
-      await onQueue({ confirmed: true });
-      setAffectedTasks([]);
-      onClose();
-    } catch (e) {
-      console.error(e);
-    }
+    await onQueue({ confirmed: true });
+    setAffectedTasks([]);
+    onClose();
   };
 
   return (
diff --git a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Clear.jsx b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Clear.jsx
index d825976ed2..de972c3f2f 100644
--- a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Clear.jsx
+++ b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Clear.jsx
@@ -65,41 +65,33 @@ const Run = ({
   });
 
   const onClick = async () => {
-    try {
-      const data = await clearTask({
-        past,
-        future,
-        upstream,
-        downstream,
-        recursive,
-        failed,
-        confirmed: false,
-        mapIndexes,
-      });
-      setAffectedTasks(data);
-      onOpen();
-    } catch (e) {
-      console.error(e);
-    }
+    const data = await clearTask({
+      past,
+      future,
+      upstream,
+      downstream,
+      recursive,
+      failed,
+      confirmed: false,
+      mapIndexes,
+    });
+    setAffectedTasks(data);
+    onOpen();
   };
 
   const onConfirm = async () => {
-    try {
-      await clearTask({
-        past,
-        future,
-        upstream,
-        downstream,
-        recursive,
-        failed,
-        confirmed: true,
-        mapIndexes,
-      });
-      setAffectedTasks([]);
-      onClose();
-    } catch (e) {
-      console.error(e);
-    }
+    await clearTask({
+      past,
+      future,
+      upstream,
+      downstream,
+      recursive,
+      failed,
+      confirmed: true,
+      mapIndexes,
+    });
+    setAffectedTasks([]);
+    onClose();
   };
 
   return (
diff --git a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkFailed.jsx b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkFailed.jsx
index 12f8bcfeef..fd75d9664d 100644
--- a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkFailed.jsx
+++ b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkFailed.jsx
@@ -63,35 +63,27 @@ const MarkFailed = ({
   });
 
   const onClick = async () => {
-    try {
-      const data = await confirmChangeMutation({
-        past,
-        future,
-        upstream,
-        downstream,
-        mapIndexes,
-      });
-      setAffectedTasks(data);
-      onOpen();
-    } catch (e) {
-      console.error(e);
-    }
+    const data = await confirmChangeMutation({
+      past,
+      future,
+      upstream,
+      downstream,
+      mapIndexes,
+    });
+    setAffectedTasks(data);
+    onOpen();
   };
 
   const onConfirm = async () => {
-    try {
-      await markFailedMutation({
-        past,
-        future,
-        upstream,
-        downstream,
-        mapIndexes,
-      });
-      setAffectedTasks([]);
-      onClose();
-    } catch (e) {
-      console.error(e);
-    }
+    await markFailedMutation({
+      past,
+      future,
+      upstream,
+      downstream,
+      mapIndexes,
+    });
+    setAffectedTasks([]);
+    onClose();
   };
 
   return (
diff --git a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkSuccess.jsx b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkSuccess.jsx
index bdf59e6a4d..515093f38d 100644
--- a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkSuccess.jsx
+++ b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkSuccess.jsx
@@ -58,35 +58,27 @@ const MarkSuccess = ({
   });
 
   const onClick = async () => {
-    try {
-      const data = await confirmChangeMutation({
-        past,
-        future,
-        upstream,
-        downstream,
-        mapIndexes,
-      });
-      setAffectedTasks(data);
-      onOpen();
-    } catch (e) {
-      console.error(e);
-    }
+    const data = await confirmChangeMutation({
+      past,
+      future,
+      upstream,
+      downstream,
+      mapIndexes,
+    });
+    setAffectedTasks(data);
+    onOpen();
   };
 
   const onConfirm = async () => {
-    try {
-      await markSuccessMutation({
-        past,
-        future,
-        upstream,
-        downstream,
-        mapIndexes,
-      });
-      setAffectedTasks([]);
-      onClose();
-    } catch (e) {
-      console.error(e);
-    }
+    await markSuccessMutation({
+      past,
+      future,
+      upstream,
+      downstream,
+      mapIndexes,
+    });
+    setAffectedTasks([]);
+    onClose();
   };
 
   return (
diff --git a/airflow/www/static/js/tree/useErrorToast.js b/airflow/www/static/js/tree/useErrorToast.js
new file mode 100644
index 0000000000..02d3195f6c
--- /dev/null
+++ b/airflow/www/static/js/tree/useErrorToast.js
@@ -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 { useToast } from '@chakra-ui/react';
+
+const getErrorDescription = (error, fallbackMessage) => {
+  if (error.response && error.response.data) {
+    return error.response.data;
+  }
+  if (error instanceof Error) return error.message;
+  if (typeof error === 'string') return error;
+  return fallbackMessage || 'Something went wrong.';
+};
+
+const getErrorTitle = (error) => (error.message || 'Error');
+
+const useErrorToast = () => {
+  const toast = useToast();
+  // Add an error prop and handle it as a description
+  return ({ error, ...rest }) => {
+    toast({
+      status: 'error',
+      title: getErrorTitle(error),
+      description: getErrorDescription(error).slice(0, 500),
+      ...rest,
+    });
+  };
+};
+
+export default useErrorToast;
diff --git a/airflow/www/views.py b/airflow/www/views.py
index ae0186e493..6113084876 100644
--- a/airflow/www/views.py
+++ b/airflow/www/views.py
@@ -513,13 +513,15 @@ def get_task_stats_from_query(qry):
     return data
 
 
-def redirect_or_json(origin, msg, status=""):
+def redirect_or_json(origin, msg, status="", status_code=200):
     """
     Some endpoints are called by javascript,
     returning json will allow us to more elegantly handle side-effects in-page
     """
     if request.headers.get('Accept') == 'application/json':
-        return {'status': status, 'message': msg}
+        if status == 'error' and status_code == 200:
+            status_code = 500
+        return Response(response=msg, status=status_code, mimetype="application/json")
     else:
         if status:
             flash(msg, status)
@@ -1757,13 +1759,13 @@ class Airflow(AirflowBaseView):
 
         if not getattr(executor, "supports_ad_hoc_ti_run", False):
             msg = "Only works with the Celery, CeleryKubernetes or Kubernetes executors"
-            return redirect_or_json(origin, msg, "error")
+            return redirect_or_json(origin, msg, "error", 400)
 
         dag_run = dag.get_dagrun(run_id=dag_run_id)
         ti = dag_run.get_task_instance(task_id=task.task_id, map_index=map_index)
         if not ti:
             msg = "Could not queue task instance for execution, task instance is missing"
-            return redirect_or_json(origin, msg, "error")
+            return redirect_or_json(origin, msg, "error", 400)
 
         ti.refresh_from_task(task)
 
@@ -1778,7 +1780,7 @@ class Airflow(AirflowBaseView):
         if failed_deps:
             failed_deps_str = ", ".join(f"{dep.dep_name}: {dep.reason}" for dep in failed_deps)
             msg = f"Could not queue task instance for execution, dependencies not met: {failed_deps_str}"
-            return redirect_or_json(origin, msg, "error")
+            return redirect_or_json(origin, msg, "error", 400)
 
         executor.job_id = "manual"
         executor.start()
@@ -1993,13 +1995,13 @@ class Airflow(AirflowBaseView):
                 dry_run=True,
             )
         except AirflowException as ex:
-            return redirect_or_json(origin, msg=str(ex), status="error")
+            return redirect_or_json(origin, msg=str(ex), status="error", status_code=500)
 
         assert isinstance(tis, collections.abc.Iterable)
         details = [str(t) for t in tis]
 
         if not details:
-            return redirect_or_json(origin, "No task instances to clear", status="error")
+            return redirect_or_json(origin, "No task instances to clear", status="error", status_code=404)
         elif request.headers.get('Accept') == 'application/json':
             return htmlsafe_json_dumps(details, separators=(',', ':'))
         return self.render_template(
@@ -2353,13 +2355,13 @@ class Airflow(AirflowBaseView):
         dag = current_app.dag_bag.get_dag(dag_id)
         if not dag:
             msg = f'DAG {dag_id} not found'
-            return redirect_or_json(origin, msg, status='error')
+            return redirect_or_json(origin, msg, status='error', status_code=404)
 
         try:
             task = dag.get_task(task_id)
         except airflow.exceptions.TaskNotFound:
             msg = f"Task {task_id} not found"
-            return redirect_or_json(origin, msg, status='error')
+            return redirect_or_json(origin, msg, status='error', status_code=404)
 
         task.dag = dag
 
@@ -2368,12 +2370,12 @@ class Airflow(AirflowBaseView):
             'failed',
         ):
             msg = f"Invalid state {state}, must be either 'success' or 'failed'"
-            return redirect_or_json(origin, msg, status='error')
+            return redirect_or_json(origin, msg, status='error', status_code=400)
 
         latest_execution_date = dag.get_latest_execution_date()
         if not latest_execution_date:
             msg = f"Cannot mark tasks as {state}, seem that dag {dag_id} has never run"
-            return redirect_or_json(origin, msg, status='error')
+            return redirect_or_json(origin, msg, status='error', status_code=400)
 
         if map_indexes is None:
             tasks: Union[List[Operator], List[Tuple[Operator, int]]] = [task]


[airflow] 01/04: Allow bulk mapped task actions

Posted by bb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

bbovenzi pushed a commit to branch fix-grid-errors
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 2fef8d505caa75518bb4652599cc816fc9577861
Author: Brent Bovenzi <br...@gmail.com>
AuthorDate: Sat Apr 9 14:50:23 2022 -0400

    Allow bulk mapped task actions
---
 airflow/www/static/js/tree/Table.jsx               |  4 ++--
 .../js/tree/details/content/taskInstance/index.jsx | 25 ++++++++++++----------
 .../content/taskInstance/taskActions/Clear.jsx     |  5 ++---
 .../taskInstance/taskActions/MarkFailed.jsx        |  6 ++----
 .../taskInstance/taskActions/MarkSuccess.jsx       | 10 ++++-----
 .../content/taskInstance/taskActions/Run.jsx       | 24 +++++++++++++++------
 6 files changed, 41 insertions(+), 33 deletions(-)

diff --git a/airflow/www/static/js/tree/Table.jsx b/airflow/www/static/js/tree/Table.jsx
index e500b08966..152f647ea3 100644
--- a/airflow/www/static/js/tree/Table.jsx
+++ b/airflow/www/static/js/tree/Table.jsx
@@ -46,7 +46,7 @@ import {
 } from 'react-icons/ti';
 
 const IndeterminateCheckbox = forwardRef(
-  ({ indeterminate, checked, ...rest }, ref) => {
+  ({ indeterminate, ...rest }, ref) => {
     const defaultRef = useRef();
     const resolvedRef = ref || defaultRef;
 
@@ -55,7 +55,7 @@ const IndeterminateCheckbox = forwardRef(
     }, [resolvedRef, indeterminate]);
 
     return (
-      <Checkbox ref={resolvedRef} isChecked={checked} {...rest} />
+      <Checkbox ref={resolvedRef} {...rest} />
     );
   },
 );
diff --git a/airflow/www/static/js/tree/details/content/taskInstance/index.jsx b/airflow/www/static/js/tree/details/content/taskInstance/index.jsx
index 0e4f441e24..62ffee156a 100644
--- a/airflow/www/static/js/tree/details/content/taskInstance/index.jsx
+++ b/airflow/www/static/js/tree/details/content/taskInstance/index.jsx
@@ -24,6 +24,7 @@ import {
   Divider,
   StackDivider,
   Text,
+  Flex,
 } from '@chakra-ui/react';
 
 import RunAction from './taskActions/Run';
@@ -88,32 +89,39 @@ const TaskInstance = ({ taskId, runId }) => {
       {!isGroup && (
         <Box my={3}>
           <Text as="strong">{taskActionsTitle}</Text>
-          <Divider my={2} />
+          <Flex maxHeight="20px" minHeight="20px">
+            {selectedRows.length ? (
+              <Text color="red.500">
+                Clear, Mark Failed, and Mark Success do not yet work with individual mapped tasks.
+              </Text>
+            ) : <Divider my={2} />}
+          </Flex>
+          {/* visibility={selectedRows.length ? 'visible' : 'hidden'} */}
           <VStack justifyContent="center" divider={<StackDivider my={3} />}>
             <RunAction
               runId={runId}
               taskId={taskId}
               dagId={dagId}
-              mapIndexes={selectedRows}
+              selectedRows={selectedRows}
             />
             <ClearAction
               runId={runId}
               taskId={taskId}
               dagId={dagId}
               executionDate={executionDate}
-              mapIndexes={selectedRows}
+              selectedRows={selectedRows}
             />
             <MarkFailedAction
               runId={runId}
               taskId={taskId}
               dagId={dagId}
-              mapIndexes={selectedRows}
+              selectedRows={selectedRows}
             />
             <MarkSuccessAction
               runId={runId}
               taskId={taskId}
               dagId={dagId}
-              mapIndexes={selectedRows}
+              selectedRows={selectedRows}
             />
           </VStack>
           <Divider my={2} />
@@ -135,12 +143,7 @@ const TaskInstance = ({ taskId, runId }) => {
         extraLinks={extraLinks}
       />
       {isMapped && (
-        <MappedInstances
-          dagId={dagId}
-          runId={runId}
-          taskId={taskId}
-          selectRows={setSelectedRows}
-        />
+        <MappedInstances dagId={dagId} runId={runId} taskId={taskId} selectRows={setSelectedRows} />
       )}
     </Box>
   );
diff --git a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Clear.jsx b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Clear.jsx
index d825976ed2..cada7b59ed 100644
--- a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Clear.jsx
+++ b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/Clear.jsx
@@ -34,7 +34,7 @@ const Run = ({
   runId,
   taskId,
   executionDate,
-  mapIndexes,
+  selectedRows,
 }) => {
   const [affectedTasks, setAffectedTasks] = useState([]);
 
@@ -74,7 +74,6 @@ const Run = ({
         recursive,
         failed,
         confirmed: false,
-        mapIndexes,
       });
       setAffectedTasks(data);
       onOpen();
@@ -93,7 +92,6 @@ const Run = ({
         recursive,
         failed,
         confirmed: true,
-        mapIndexes,
       });
       setAffectedTasks([]);
       onClose();
@@ -116,6 +114,7 @@ const Run = ({
         colorScheme="blue"
         onClick={onClick}
         isLoading={isLoading}
+        isDisabled={!!selectedRows.length}
         title="Clearing deletes the previous state of the task instance, allowing it to get re-triggered by the scheduler or a backfill command"
       >
         Clear
diff --git a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkFailed.jsx b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkFailed.jsx
index 12f8bcfeef..6bc10c066e 100644
--- a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkFailed.jsx
+++ b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkFailed.jsx
@@ -33,7 +33,7 @@ const MarkFailed = ({
   dagId,
   runId,
   taskId,
-  mapIndexes,
+  selectedRows,
 }) => {
   const [affectedTasks, setAffectedTasks] = useState([]);
 
@@ -69,7 +69,6 @@ const MarkFailed = ({
         future,
         upstream,
         downstream,
-        mapIndexes,
       });
       setAffectedTasks(data);
       onOpen();
@@ -85,7 +84,6 @@ const MarkFailed = ({
         future,
         upstream,
         downstream,
-        mapIndexes,
       });
       setAffectedTasks([]);
       onClose();
@@ -102,7 +100,7 @@ const MarkFailed = ({
         <ActionButton bg={upstream && 'gray.100'} onClick={onToggleUpstream} name="Upstream" />
         <ActionButton bg={downstream && 'gray.100'} onClick={onToggleDownstream} name="Downstream" />
       </ButtonGroup>
-      <Button colorScheme="red" onClick={onClick} isLoading={isMarkLoading || isConfirmLoading}>
+      <Button colorScheme="red" onClick={onClick} isLoading={isMarkLoading || isConfirmLoading} isDisabled={!!selectedRows.length}>
         Mark Failed
       </Button>
       <ConfirmDialog
diff --git a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkSuccess.jsx b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkSuccess.jsx
index bdf59e6a4d..b4d2b8c047 100644
--- a/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkSuccess.jsx
+++ b/airflow/www/static/js/tree/details/content/taskInstance/taskActions/MarkSuccess.jsx
@@ -29,8 +29,8 @@ import ConfirmDialog from '../../ConfirmDialog';
 import ActionButton from './ActionButton';
 import { useMarkSuccessTask, useConfirmMarkTask } from '../../../../api';
 
-const MarkSuccess = ({
-  dagId, runId, taskId, mapIndexes,
+const Run = ({
+  dagId, runId, taskId, selectedRows,
 }) => {
   const [affectedTasks, setAffectedTasks] = useState([]);
 
@@ -64,7 +64,6 @@ const MarkSuccess = ({
         future,
         upstream,
         downstream,
-        mapIndexes,
       });
       setAffectedTasks(data);
       onOpen();
@@ -80,7 +79,6 @@ const MarkSuccess = ({
         future,
         upstream,
         downstream,
-        mapIndexes,
       });
       setAffectedTasks([]);
       onClose();
@@ -97,7 +95,7 @@ const MarkSuccess = ({
         <ActionButton bg={upstream && 'gray.100'} onClick={onToggleUpstream} name="Upstream" />
         <ActionButton bg={downstream && 'gray.100'} onClick={onToggleDownstream} name="Downstream" />
       </ButtonGroup>
-      <Button colorScheme="green" onClick={onClick} isLoading={isMarkLoading || isConfirmLoading}>
+      <Button colorScheme="green" onClick={onClick} isLoading={isMarkLoading || isConfirmLoading} isDisabled={!!selectedRows.length}>
         Mark Success
       </Button>
       <ConfirmDialog
@@ -111,4 +109,4 @@ const MarkSuccess = ({
   );
 };
 
-export default MarkSuccess;
+export default Run;
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 85d502aeed..41c8bb9c6f 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
@@ -30,7 +30,7 @@ const Run = ({
   dagId,
   runId,
   taskId,
-  mapIndexes,
+  selectedRows,
 }) => {
   const [ignoreAllDeps, setIgnoreAllDeps] = useState(false);
   const onToggleAllDeps = () => setIgnoreAllDeps(!ignoreAllDeps);
@@ -44,12 +44,22 @@ const Run = ({
   const { mutate: onRun, isLoading } = useRunTask(dagId, runId, taskId);
 
   const onClick = () => {
-    onRun({
-      ignoreAllDeps,
-      ignoreTaskState,
-      ignoreTaskDeps,
-      mapIndexes,
-    });
+    if (selectedRows.length) {
+      selectedRows.forEach((mapIndex) => {
+        onRun({
+          ignoreAllDeps,
+          ignoreTaskState,
+          ignoreTaskDeps,
+          mapIndex,
+        });
+      });
+    } else {
+      onRun({
+        ignoreAllDeps,
+        ignoreTaskState,
+        ignoreTaskDeps,
+      });
+    }
   };
 
   return (


[airflow] 03/04: Fix gantt/graph modal

Posted by bb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

bbovenzi pushed a commit to branch fix-grid-errors
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 088929deac42ae1391b7cee6b292cddcbaf1641e
Author: Brent Bovenzi <br...@gmail.com>
AuthorDate: Tue Apr 12 16:01:31 2022 -0400

    Fix gantt/graph modal
---
 airflow/www/static/js/dag.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/airflow/www/static/js/dag.js b/airflow/www/static/js/dag.js
index 0445e0686c..67031fba2b 100644
--- a/airflow/www/static/js/dag.js
+++ b/airflow/www/static/js/dag.js
@@ -352,6 +352,9 @@ $('form[data-action]').on('submit', function submit(e) {
     } else if (form.map_index) {
       form.map_index.remove();
     }
+    if (form.map_indexes) {
+      form.map_indexes.value = mapIndex === undefined ? '' : mapIndex;
+    }
     form.action = $(this).data('action');
     form.submit();
   }