Skip to content

Commit

Permalink
Schedule rerun time (#77)
Browse files Browse the repository at this point in the history
* added scheduling option for rerunning task
  • Loading branch information
vegarrsm authored Sep 29, 2023
1 parent abaf451 commit acf76af
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,4 @@ jobs:
dockerfile_directory: ./example-app/
dockerfile_name: Dockerfile
docker_options: "--no-cache"
process_type: web
process_type: web
4 changes: 2 additions & 2 deletions db-scheduler-ui-frontend/src/components/common/HeaderBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
Button,
} from '@chakra-ui/react';
import { FilterBy } from 'src/models/QueryParams';
import { FilterBox } from '../input/FilterBox';
import { FilterBox } from 'src/components/input/FilterBox';
import { RefreshButton } from 'src/components/input/RefreshButton';
import { QueryObserverResult, InfiniteData } from '@tanstack/react-query';
import { InfiniteScrollResponse } from 'src/models/TasksResponse';
Expand All @@ -32,7 +32,7 @@ import { POLL_LOGS_QUERY_KEY, pollLogs } from 'src/services/pollLogs';
import { POLL_TASKS_QUERY_KEY, pollTasks } from 'src/services/pollTasks';
import { PlayIcon, RepeatIcon } from 'src/assets/icons';
import colors from 'src/styles/colors';
import { RunAllAlert } from './RunAllAlert';
import { RunAllAlert } from 'src/components/scheduled/RunAllAlert';
import { TaskDetailsRequestParams } from 'src/models/TaskRequestParams';
import { useParams } from 'react-router-dom';

Expand Down
12 changes: 12 additions & 0 deletions db-scheduler-ui-frontend/src/components/input/DateTimeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ import colors from 'src/styles/colors';
interface DateTimeInputProps {
selectedDate: Date | null;
onChange: (date: Date | null) => void;
forceFutureTime?: boolean;
}

export const DateTimeInput: React.FC<DateTimeInputProps> = ({
selectedDate,
onChange,
forceFutureTime,
}) => {
return (
<Box
Expand All @@ -32,6 +34,7 @@ export const DateTimeInput: React.FC<DateTimeInputProps> = ({
borderColor={colors.primary[300]}
borderWidth={1}
backgroundColor={colors.primary[100]}
w="fit-content"
>
<DatePicker
selected={selectedDate}
Expand All @@ -41,6 +44,15 @@ export const DateTimeInput: React.FC<DateTimeInputProps> = ({
dateFormat={'yyyy-MM-dd HH:mm'}
placeholderText={'YYYY-MM-DD HH:mm'}
useWeekdaysShort
minTime={
selectedDate?.toDateString() === new Date().toDateString() &&
forceFutureTime
? new Date()
: new Date(0, 0, 1)
}
maxTime={new Date(2100, 0, 1, 23, 59, 59, 999)}
minDate={forceFutureTime ? new Date() : new Date(0)}
maxDate={new Date(2100, 0, 1)}
></DatePicker>
</Box>
);
Expand Down
33 changes: 28 additions & 5 deletions db-scheduler-ui-frontend/src/components/input/DotButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,28 @@ import {
} from '@chakra-ui/react';
import deleteTask from 'src/services/deleteTask';
import React from 'react';
import { DeleteIcon, InfoOutlineIcon } from '@chakra-ui/icons';
import { CalendarIcon, DeleteIcon, InfoOutlineIcon } from '@chakra-ui/icons';
import { IoEllipsisVerticalIcon } from '../../assets/icons';
import { useNavigate } from 'react-router-dom';
import { ScheduleRunAlert } from './ScheduleRunAlert';

interface TaskProps {
taskName: string;
taskInstance: string;
style?: React.CSSProperties;
refetch?: () => void;
}

export const DotButton: React.FC<TaskProps> = ({
taskName,
taskInstance,
style,
refetch,
}) => {
const [isOpen, setIsOpen] = React.useState(false);
const [deleteOpen, setDeleteOpen] = React.useState(false);
const [scheduleOpen, setScheduleOpen] = React.useState(false);
const navigate = useNavigate();
const onClose = () => setIsOpen(false);
const onClose = () => setDeleteOpen(false);
const cancelRef = React.useRef(null);
return (
<Box style={style}>
Expand All @@ -60,6 +64,17 @@ export const DotButton: React.FC<TaskProps> = ({
/>

<MenuList padding={0}>
<MenuItem
rounded={6}
minBlockSize={10}
onClick={(event) => {
event.stopPropagation();
setScheduleOpen(true);
}}
icon={<CalendarIcon boxSize={4} />}
>
Update execution time
</MenuItem>
<MenuItem
rounded={6}
minBlockSize={10}
Expand All @@ -76,16 +91,24 @@ export const DotButton: React.FC<TaskProps> = ({
minBlockSize={10}
onClick={(event) => {
event.stopPropagation();
setIsOpen(true);
setDeleteOpen(true);
}}
icon={<DeleteIcon boxSize={4} />}
>
Delete task
</MenuItem>
</MenuList>
</Menu>
<ScheduleRunAlert
failed={true}
isOpen={scheduleOpen}
setIsopen={setScheduleOpen}
taskId={taskInstance}
taskName={taskName}
refetch={refetch ?? (() => {})}
/>
<AlertDialog
isOpen={isOpen}
isOpen={deleteOpen}
leastDestructiveRef={cancelRef}
onClose={onClose}
>
Expand Down
105 changes: 105 additions & 0 deletions db-scheduler-ui-frontend/src/components/input/ScheduleRunAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright (C) Bekk
*
* <p>Licensed 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
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>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 {
AlertDialog,
AlertDialogBody,
AlertDialogContent,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogOverlay,
Button,
} from '@chakra-ui/react';
import React from 'react';
import colors from 'src/styles/colors';
import { DateTimeInput } from './DateTimeInput';
import runTask from 'src/services/runTask';

interface TaskProps {
failed: boolean;
taskName: string;
taskId: string;
isOpen: boolean;
setIsopen: React.Dispatch<React.SetStateAction<boolean>>;
refetch: () => void;
}

export const ScheduleRunAlert: React.FC<TaskProps> = ({
failed,
isOpen,
taskId,
taskName,
setIsopen,
refetch,
}) => {
const cancelRef = React.useRef(null);
const [time, setTime] = React.useState<Date | null>(new Date());

return (
<AlertDialog
isOpen={isOpen}
leastDestructiveRef={cancelRef}
onClose={() => setIsopen(false)}
>
<AlertDialogOverlay>
<AlertDialogContent>
<AlertDialogHeader fontSize="lg" fontWeight="bold">
{failed ? 'Rerun Task At Time' : 'Run Task At Time'}
</AlertDialogHeader>

<AlertDialogBody p={4}>
<DateTimeInput
onChange={(e) => setTime(e)}
selectedDate={time}
forceFutureTime
/>
</AlertDialogBody>

<AlertDialogFooter justifyContent={'space-between'}>
<Button
onClick={() => {
setIsopen(false);
}}
>
Cancel
</Button>
<Button
bgColor={failed ? colors.running[300] : colors.running[100]}
_hover={{
backgroundColor: failed
? colors.running[200]
: colors.running[100],
}}
_active={{
backgroundColor: failed
? colors.running[200]
: colors.running[300],
}}
textColor={failed ? colors.primary[100] : colors.running[500]}
onClick={() => {
time &&
runTask(taskId, taskName, time).then(() => {
refetch();
});
setIsopen(false);
}}
ml={3}
>
{failed ? 'Update Rerun Time' : 'Update Run Time'}
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialogOverlay>
</AlertDialog>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export const TaskAccordionButton: React.FC<TaskAccordionButtonProps> = (
? 'visible'
: 'hidden',
}}
refetch={refetch}
/>
</Box>
<AccordionIcon
Expand Down
11 changes: 9 additions & 2 deletions db-scheduler-ui-frontend/src/services/runTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,16 @@ const API_BASE_URL: string =
(import.meta.env.VITE_API_BASE_URL as string) ??
window.location.origin + '/db-scheduler-api';

const runTask = async (id: string, name: string) => {
const runTask = async (id: string, name: string, scheduleTime?:Date) => {

const queryParams = new URLSearchParams();

queryParams.append('id', id);
queryParams.append('name', name);
scheduleTime&&queryParams.append('scheduleTime', scheduleTime.toISOString());

const response = await fetch(
`${API_BASE_URL}/tasks/rerun?id=${id}&name=${name}`,
`${API_BASE_URL}/tasks/rerun?${queryParams}`,
{
method: 'POST',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/
package no.bekk.dbscheduler.ui.controller;

import java.time.Instant;
import no.bekk.dbscheduler.ui.model.GetTasksResponse;
import no.bekk.dbscheduler.ui.model.PollResponse;
import no.bekk.dbscheduler.ui.model.TaskDetailsRequestParams;
Expand Down Expand Up @@ -48,8 +49,9 @@ public PollResponse pollForUpdates(TaskDetailsRequestParams params) {
}

@PostMapping("/rerun")
public void runNow(@RequestParam String id, @RequestParam String name) {
taskLogic.runTaskNow(id, name);
public void runNow(
@RequestParam String id, @RequestParam String name, @RequestParam Instant scheduleTime) {
taskLogic.runTaskNow(id, name, scheduleTime);
}

@PostMapping("/rerunGroup")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@ public TaskLogic(Scheduler scheduler, Caching caching, boolean showData) {
this.showData = showData;
}

public void runTaskNow(String taskId, String taskName) {
public void runTaskNow(String taskId, String taskName, Instant scheduleTime) {
Optional<ScheduledExecution<Object>> scheduledExecutionOpt =
scheduler.getScheduledExecution(TaskInstanceId.of(taskName, taskId));

if (scheduledExecutionOpt.isPresent() && !scheduledExecutionOpt.get().isPicked()) {
scheduler.reschedule(scheduledExecutionOpt.get().getTaskInstance(), Instant.now());
scheduler.reschedule(
scheduledExecutionOpt.get().getTaskInstance(),
scheduleTime != null ? scheduleTime : Instant.now());
} else {
throw new ResponseStatusException(
HttpStatus.NOT_FOUND,
Expand All @@ -68,7 +70,8 @@ public void runTaskGroupNow(String taskName, boolean onlyFailed) {
try {
runTaskNow(
execution.getTaskInstance().getId(),
execution.getTaskInstance().getTaskName());
execution.getTaskInstance().getTaskName(),
Instant.now());
} catch (ResponseStatusException e) {
System.out.println("Failed to run task: " + e.getMessage());
}
Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -383,4 +383,4 @@
</build>
</profile>
</profiles>
</project>
</project>

0 comments on commit acf76af

Please sign in to comment.