You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by je...@apache.org on 2022/04/21 17:27:06 UTC
[airflow] branch main updated: Fix error handling in Grid view (#23152)
This is an automated email from the ASF dual-hosted git repository.
jedcunningham pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new c6b27e309c Fix error handling in Grid view (#23152)
c6b27e309c is described below
commit c6b27e309cbc29c73604969c88c8e8b437af583f
Author: Brent Bovenzi <br...@gmail.com>
AuthorDate: Thu Apr 21 13:26:56 2022 -0400
Fix error handling in Grid view (#23152)
---
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]