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/03/08 20:57:19 UTC

[airflow] 10/11: dag run actions

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

bbovenzi pushed a commit to branch mapped-task-drawer
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit d86f6984393a45b74324943f04f47e8b808f61e2
Author: Brent Bovenzi <br...@gmail.com>
AuthorDate: Tue Mar 8 13:40:23 2022 -0500

    dag run actions
---
 airflow/www/static/js/tree/Tree.jsx                |  12 +-
 airflow/www/static/js/tree/api/index.js            |   1 -
 airflow/www/static/js/tree/api/useDag.js           |   2 +-
 airflow/www/static/js/tree/api/useTasks.js         |   2 +-
 .../www/static/js/tree/details/content/DagRun.jsx  | 204 ++++++++++++++-------
 airflow/www/templates/airflow/dag.html             |   1 +
 6 files changed, 148 insertions(+), 74 deletions(-)

diff --git a/airflow/www/static/js/tree/Tree.jsx b/airflow/www/static/js/tree/Tree.jsx
index 0d84639..b777d73 100644
--- a/airflow/www/static/js/tree/Tree.jsx
+++ b/airflow/www/static/js/tree/Tree.jsx
@@ -53,8 +53,12 @@ const Tree = () => {
   const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: isPanelOpen });
 
   const toggleSidePanel = () => {
-    if (!isOpen) localStorage.setItem(sidePanelKey, true);
-    else localStorage.setItem(sidePanelKey, false);
+    if (!isOpen) {
+      localStorage.setItem(sidePanelKey, true);
+    } else {
+      setSelected({});
+      localStorage.setItem(sidePanelKey, false);
+    }
     onToggle();
   };
 
@@ -123,8 +127,8 @@ const Tree = () => {
           pb="12px"
           overflowX="auto"
           ref={scrollRef}
-          maxWidth={isOpen && '300px'}
-          minWidth={isOpen && '300px'}
+          flexGrow={1}
+          width={isOpen && '300px'}
         >
           <Table height={0}>
             <Thead>
diff --git a/airflow/www/static/js/tree/api/index.js b/airflow/www/static/js/tree/api/index.js
index 58edd88..3327fef 100644
--- a/airflow/www/static/js/tree/api/index.js
+++ b/airflow/www/static/js/tree/api/index.js
@@ -23,7 +23,6 @@ import camelcaseKeys from 'camelcase-keys';
 import useDag from './useDag';
 import useTasks from './useTasks';
 
-axios.defaults.baseURL = '/api/v1';
 axios.interceptors.response.use(
   (res) => (res.data ? camelcaseKeys(res.data, { deep: true }) : res),
 );
diff --git a/airflow/www/static/js/tree/api/useDag.js b/airflow/www/static/js/tree/api/useDag.js
index 1343302..143bbe0 100644
--- a/airflow/www/static/js/tree/api/useDag.js
+++ b/airflow/www/static/js/tree/api/useDag.js
@@ -23,6 +23,6 @@ import { useQuery } from 'react-query';
 export default function useDag(dagId) {
   return useQuery(
     ['dag', dagId],
-    () => axios.get(`/dags/${dagId}/details`),
+    () => axios.get(`/api/v1/dags/${dagId}/details`),
   );
 }
diff --git a/airflow/www/static/js/tree/api/useTasks.js b/airflow/www/static/js/tree/api/useTasks.js
index 3dd11e30..5edb137 100644
--- a/airflow/www/static/js/tree/api/useTasks.js
+++ b/airflow/www/static/js/tree/api/useTasks.js
@@ -23,6 +23,6 @@ import { useQuery } from 'react-query';
 export default function useTasks(dagId) {
   return useQuery(
     ['tasks', dagId],
-    () => axios.get(`/dags/${dagId}/tasks`),
+    () => axios.get(`/api/v1/dags/${dagId}/tasks`),
   );
 }
diff --git a/airflow/www/static/js/tree/details/content/DagRun.jsx b/airflow/www/static/js/tree/details/content/DagRun.jsx
index abcf908..be06794 100644
--- a/airflow/www/static/js/tree/details/content/DagRun.jsx
+++ b/airflow/www/static/js/tree/details/content/DagRun.jsx
@@ -20,83 +20,153 @@
 /* global moment */
 
 import React from 'react';
+import axios from 'axios';
 import {
+  Flex,
   Text,
   Box,
+  Button,
 } from '@chakra-ui/react';
 import { MdPlayArrow } from 'react-icons/md';
 
 import { formatDateTime, formatDuration } from '../../../datetime_utils';
+import { getMetaValue } from '../../../utils';
 
 const DagRun = ({
   dagRun: {
-    state, runId, duration, dataIntervalStart, dataIntervalEnd, startDate, endDate, runType,
+    dagId, state, runId, duration, dataIntervalStart, dataIntervalEnd, startDate, endDate, runType,
   },
-}) => (
-  <Box fontSize="12px" py="4px">
-    <Text>
-      <Text as="strong">Status:</Text>
-      {' '}
-      {state || 'no status'}
-    </Text>
-    <br />
-    <Text whiteSpace="nowrap">
-      Run Id:
-      {' '}
-      {runId}
-    </Text>
-    <Text>
-      Run Type:
-      {' '}
-      {runType === 'manual' && <MdPlayArrow style={{ display: 'inline' }} />}
-      {runType}
-    </Text>
-    <Text>
-      Duration:
-      {' '}
-      {formatDuration(duration)}
-    </Text>
-    <br />
-    <Text as="strong">Data Interval:</Text>
-    <Text>
-      Start:
-      {' '}
-      {formatDateTime(dataIntervalStart)}
-    </Text>
-    <Text>
-      End:
-      {' '}
-      {formatDateTime(dataIntervalEnd)}
-    </Text>
-    <br />
-    <Text as="strong">UTC</Text>
-    <Text>
-      Started:
-      {' '}
-      {formatDateTime(moment.utc(startDate))}
-    </Text>
-    <Text>
-      Ended:
-      {' '}
-      {endDate && formatDateTime(moment.utc(endDate))}
-    </Text>
-    <br />
-    <Text as="strong">
-      Local:
-      {' '}
-      {moment().format('Z')}
-    </Text>
-    <Text>
-      Started:
-      {' '}
-      {formatDateTime(startDate)}
-    </Text>
-    <Text>
-      Ended:
-      {' '}
-      {endDate && formatDateTime(endDate)}
-    </Text>
-  </Box>
-);
+}) => {
+  const csrfToken = getMetaValue('csrf_token');
+
+  const onClear = async () => {
+    const params = new URLSearchParams({
+      csrf_token: csrfToken,
+      confirmed: true,
+      dag_id: dagId,
+      dag_run_id: runId,
+    }).toString();
+
+    try {
+      await axios.post('/dagrun_clear', params, {
+        headers: {
+          'Content-Type': 'application/x-www-form-urlencoded',
+        },
+      });
+    } catch (e) {
+      console.error(e);
+    }
+  };
+
+  const markFailed = async () => {
+    const params = new URLSearchParams({
+      csrf_token: csrfToken,
+      confirmed: true,
+      dag_id: dagId,
+      dag_run_id: runId,
+    }).toString();
+
+    try {
+      await axios.post('/dagrun_failed', params, {
+        headers: {
+          'Content-Type': 'application/x-www-form-urlencoded',
+        },
+      });
+    } catch (e) {
+      console.error(e);
+    }
+  };
+
+  const markSuccess = async () => {
+    const params = new URLSearchParams({
+      csrf_token: csrfToken,
+      confirmed: true,
+      dag_id: dagId,
+      dag_run_id: runId,
+    }).toString();
+
+    try {
+      await axios.post('/dagrun_success', params, {
+        headers: {
+          'Content-Type': 'application/x-www-form-urlencoded',
+        },
+      });
+    } catch (e) {
+      console.error(e);
+    }
+  };
+
+  return (
+    <Box fontSize="12px" py="4px">
+      <Flex justifyContent="space-evenly">
+        <Button onClick={onClear}>Clear</Button>
+        <Button onClick={markFailed} colorScheme="red">Mark Failed</Button>
+        <Button onClick={markSuccess} colorScheme="green">Mark Success</Button>
+      </Flex>
+      <Text>
+        <Text as="strong">Status:</Text>
+        {' '}
+        {state || 'no status'}
+      </Text>
+      <br />
+      <Text whiteSpace="nowrap">
+        Run Id:
+        {' '}
+        {runId}
+      </Text>
+      <Text>
+        Run Type:
+        {' '}
+        {runType === 'manual' && <MdPlayArrow style={{ display: 'inline' }} />}
+        {runType}
+      </Text>
+      <Text>
+        Duration:
+        {' '}
+        {formatDuration(duration)}
+      </Text>
+      <br />
+      <Text as="strong">Data Interval:</Text>
+      <Text>
+        Start:
+        {' '}
+        {formatDateTime(dataIntervalStart)}
+      </Text>
+      <Text>
+        End:
+        {' '}
+        {formatDateTime(dataIntervalEnd)}
+      </Text>
+      <br />
+      <Text as="strong">UTC</Text>
+      <Text>
+        Started:
+        {' '}
+        {formatDateTime(moment.utc(startDate))}
+      </Text>
+      <Text>
+        Ended:
+        {' '}
+        {endDate && formatDateTime(moment.utc(endDate))}
+      </Text>
+      <br />
+      <Text as="strong">
+        Local:
+        {' '}
+        {moment().format('Z')}
+      </Text>
+      <Text>
+        Started:
+        {' '}
+        {formatDateTime(startDate)}
+      </Text>
+      <Text>
+        Ended:
+        {' '}
+        {endDate && formatDateTime(endDate)}
+      </Text>
+    </Box>
+  );
+};
 
 export default DagRun;
diff --git a/airflow/www/templates/airflow/dag.html b/airflow/www/templates/airflow/dag.html
index f2444abab..0f3387a 100644
--- a/airflow/www/templates/airflow/dag.html
+++ b/airflow/www/templates/airflow/dag.html
@@ -39,6 +39,7 @@
   <meta name="paused_url" content="{{ url_for('Airflow.paused') }}">
   <meta name="tree_data" content="{{ url_for('Airflow.tree_data') }}">
   <meta name="is_paused" content="{{ dag_is_paused }}">
+  <meta name="csrf_token" content="{{ csrf_token() }}">
   {% if dag_model is defined and dag_model.next_dagrun_create_after is defined and dag_model.next_dagrun_create_after is not none %}
     <meta name="next_dagrun_create_after" content="{{ dag_model.next_dagrun_create_after }}">
     <meta name="next_dagrun_data_interval_start" content="{{ dag_model.next_dagrun_data_interval_start }}">