devtools: Add cancel task operation to scheduled tasks
Renames useTriggerScheduledTask to useScheduledTasksOperations and adds cancelTask alongside triggerTask, with shared isLoading/error state. Adds CancelScheduledTask type to devtools-common and cancelScheduledTask to the DevToolsApi interface and client, hitting the existing scheduler cancel endpoint. The ScheduledTasksContent UI now shows both trigger and cancel buttons per task row. Signed-off-by: Fredrik Adelöw <freben@spotify.com> Made-with: Cursor
This commit is contained in:
@@ -21,6 +21,7 @@ import {
|
||||
ExternalDependency,
|
||||
} from '@backstage/plugin-devtools-common';
|
||||
import {
|
||||
CancelScheduledTask,
|
||||
ScheduledTasks,
|
||||
TriggerScheduledTask,
|
||||
} from '@backstage/plugin-devtools-common/alpha';
|
||||
@@ -38,4 +39,8 @@ export interface DevToolsApi {
|
||||
plugin: string,
|
||||
taskId: string,
|
||||
): Promise<TriggerScheduledTask>;
|
||||
cancelScheduledTask(
|
||||
plugin: string,
|
||||
taskId: string,
|
||||
): Promise<CancelScheduledTask>;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
ExternalDependency,
|
||||
} from '@backstage/plugin-devtools-common';
|
||||
import {
|
||||
CancelScheduledTask,
|
||||
ScheduledTasks,
|
||||
TriggerScheduledTask,
|
||||
} from '@backstage/plugin-devtools-common/alpha';
|
||||
@@ -85,6 +86,27 @@ export class DevToolsClient implements DevToolsApi {
|
||||
return response.json() as Promise<TriggerScheduledTask>;
|
||||
}
|
||||
|
||||
public async cancelScheduledTask(
|
||||
plugin: string,
|
||||
taskId: string,
|
||||
): Promise<CancelScheduledTask> {
|
||||
const baseUrl = `${await this.discoveryApi.getBaseUrl(plugin)}/`;
|
||||
const url = new URL(
|
||||
`.backstage/scheduler/v1/tasks/${encodeURIComponent(taskId)}/cancel`,
|
||||
baseUrl,
|
||||
);
|
||||
|
||||
const response = await this.fetchApi.fetch(url.toString(), {
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw await ResponseError.fromResponse(response);
|
||||
}
|
||||
|
||||
return response.json() as Promise<CancelScheduledTask>;
|
||||
}
|
||||
|
||||
public async getExternalDependencies(): Promise<
|
||||
ExternalDependency[] | undefined
|
||||
> {
|
||||
|
||||
+49
-24
@@ -29,10 +29,11 @@ import {
|
||||
TableColumn,
|
||||
} from '@backstage/core-components';
|
||||
import Alert from '@material-ui/lab/Alert';
|
||||
import { useScheduledTasks, useTriggerScheduledTask } from '../../../hooks';
|
||||
import { useScheduledTasks, useScheduledTasksOperations } from '../../../hooks';
|
||||
import { TaskApiTasksResponse } from '@backstage/plugin-devtools-common/alpha';
|
||||
import { alertApiRef, configApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import RefreshIcon from '@material-ui/icons/Refresh';
|
||||
import StopIcon from '@material-ui/icons/Stop';
|
||||
import NightsStay from '@material-ui/icons/NightsStay';
|
||||
import ErrorIcon from '@material-ui/icons/Error';
|
||||
import BlockIcon from '@material-ui/icons/Block';
|
||||
@@ -105,7 +106,7 @@ export const ScheduledTasksContent = () => {
|
||||
configApi.getOptionalStringArray('devTools.scheduledTasks.plugins') || [];
|
||||
const [selectedPlugin, setSelectedPlugin] = useState(plugins[0] || '');
|
||||
const { scheduledTasks, loading, error } = useScheduledTasks(selectedPlugin);
|
||||
const { triggerTask, isTriggering, triggerError } = useTriggerScheduledTask();
|
||||
const { triggerTask, cancelTask, isLoading } = useScheduledTasksOperations();
|
||||
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
|
||||
@@ -209,28 +210,52 @@ export const ScheduledTasksContent = () => {
|
||||
permission={devToolsTaskSchedulerCreatePermission}
|
||||
errorPage={<CreateNotAllowed />}
|
||||
>
|
||||
<Tooltip title="Run Task">
|
||||
<IconButton
|
||||
aria-label="Trigger"
|
||||
disabled={isTriggering}
|
||||
onClick={() => {
|
||||
triggerTask(selectedPlugin, rowData.taskId);
|
||||
if (triggerError) {
|
||||
alertApi.post({
|
||||
message: `Error triggering task ${rowData.taskId}: ${error}`,
|
||||
severity: 'error',
|
||||
});
|
||||
} else {
|
||||
alertApi.post({
|
||||
message: `Successfully triggered task ${rowData.taskId}`,
|
||||
severity: 'success',
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<RefreshIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Box display="flex" justifyContent="center">
|
||||
<Tooltip title="Run Task">
|
||||
<IconButton
|
||||
aria-label="Trigger"
|
||||
disabled={isLoading}
|
||||
onClick={async () => {
|
||||
try {
|
||||
await triggerTask(selectedPlugin, rowData.taskId);
|
||||
alertApi.post({
|
||||
message: `Successfully triggered task ${rowData.taskId}`,
|
||||
severity: 'success',
|
||||
});
|
||||
} catch (e) {
|
||||
alertApi.post({
|
||||
message: `Error triggering task ${rowData.taskId}: ${e.message}`,
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<RefreshIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title="Cancel Task">
|
||||
<IconButton
|
||||
aria-label="Cancel"
|
||||
disabled={isLoading}
|
||||
onClick={async () => {
|
||||
try {
|
||||
await cancelTask(selectedPlugin, rowData.taskId);
|
||||
alertApi.post({
|
||||
message: `Successfully cancelled task ${rowData.taskId}`,
|
||||
severity: 'success',
|
||||
});
|
||||
} catch (e) {
|
||||
alertApi.post({
|
||||
message: `Error cancelling task ${rowData.taskId}: ${e.message}`,
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<StopIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
</RequirePermission>
|
||||
),
|
||||
sorting: false,
|
||||
|
||||
@@ -18,4 +18,4 @@ export { useConfig } from './useConfig';
|
||||
export { useExternalDependencies } from './useExternalDependencies';
|
||||
export { useInfo } from './useInfo';
|
||||
export { useScheduledTasks } from './useScheduledTasks';
|
||||
export { useTriggerScheduledTask } from './useTriggerScheduledTask';
|
||||
export { useScheduledTasksOperations } from './useScheduledTasksOperations';
|
||||
|
||||
+25
-6
@@ -17,22 +17,40 @@ import { useState, useCallback } from 'react';
|
||||
import { devToolsApiRef } from '../api';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
export const useTriggerScheduledTask = () => {
|
||||
export const useScheduledTasksOperations = () => {
|
||||
const api = useApi(devToolsApiRef);
|
||||
const [isTriggering, setIsTriggering] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<Error | undefined>();
|
||||
|
||||
const triggerTask = useCallback(
|
||||
async (plugin: string, taskId: string) => {
|
||||
setIsTriggering(true);
|
||||
setIsLoading(true);
|
||||
setError(undefined);
|
||||
|
||||
try {
|
||||
await api.triggerScheduledTask(plugin, taskId);
|
||||
} catch (e) {
|
||||
setError(e);
|
||||
throw e;
|
||||
} finally {
|
||||
setIsTriggering(false);
|
||||
setIsLoading(false);
|
||||
}
|
||||
},
|
||||
[api],
|
||||
);
|
||||
|
||||
const cancelTask = useCallback(
|
||||
async (plugin: string, taskId: string) => {
|
||||
setIsLoading(true);
|
||||
setError(undefined);
|
||||
|
||||
try {
|
||||
await api.cancelScheduledTask(plugin, taskId);
|
||||
} catch (e) {
|
||||
setError(e);
|
||||
throw e;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
},
|
||||
[api],
|
||||
@@ -40,7 +58,8 @@ export const useTriggerScheduledTask = () => {
|
||||
|
||||
return {
|
||||
triggerTask,
|
||||
isTriggering,
|
||||
triggerError: error?.message,
|
||||
cancelTask,
|
||||
isLoading,
|
||||
error: error?.message,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user