Skip to content

Commit

Permalink
Add interval refetch over a duration
Browse files Browse the repository at this point in the history
  • Loading branch information
Richard87 committed Sep 12, 2024
1 parent 2c17157 commit 55f7c9a
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export const ActiveComponentOverview: FunctionComponent<{
component={component}
appName={appName}
envName={envName}
refetch={refetch}
/>

<ComponentStatus
Expand Down
126 changes: 67 additions & 59 deletions src/components/page-active-component/active-component-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,72 +14,100 @@ import {
} from '../../store/radix-api';
import { handlePromiseWithToast } from '../global-top-nav/styled-toaster';
import './style.css';
import { useDurationInterval } from '../../effects/use-interval';

type Props = {
component: Component;
appName: string;
envName: string;
refetch: () => unknown;
};

export function ActiveComponentToolbar({ component, appName, envName }: Props) {
const [restartTrigger, restartState] = useRestartComponentMutation();
export function ActiveComponentToolbar({
component,
appName,
envName,
refetch,
}: Props) {
const [scaleTrigger, scaleState] = useScaleComponentMutation();
const [stopTrigger, stopState] = useStopComponentMutation();
const [restartTrigger, restartState] = useRestartComponentMutation();
const startRefetch = useDurationInterval(2_000, 30_000, refetch);

const isStopped = component?.status === 'Stopped';
const restartInProgress =
const isWorking =
restartState.isLoading ||
scaleState.isLoading ||
stopState.isLoading ||
component?.status === 'Reconciling' ||
component?.status === 'Restarting';

const onStop = handlePromiseWithToast(
async () => {
await stopTrigger({
appName,
envName,
componentName: component.name,
}).unwrap();
startRefetch();
},
'Stopping component',
'Failed to stop component'
);

const onRestart = handlePromiseWithToast(
async () => {
await restartTrigger({
appName,
envName,
componentName: component.name,
}).unwrap();
startRefetch();
},
'Restarting component',
'Failed to restart component'
);

const onScale = handlePromiseWithToast(
async (replicas: number) => {
await scaleTrigger({
appName,
envName,
componentName: component.name,
replicas: replicas.toFixed(),
}).unwrap();
startRefetch();
},
'Scaling component',
'Failed to scale component'
);
return (
<>
<div className="grid grid--gap-small">
<div className="grid grid--gap-small grid--auto-columns">
<ScaleButtonPopup
disabled={false}
appName={appName}
envName={envName}
componentName={component.name}
onScale={onScale}
disabled={scaleState.isLoading}
currentReplicas={
component.replicasOverride ?? component.replicaList.length
}
/>
<Button
disabled={isStopped || stopState.isLoading}
variant="outlined"
onClick={() =>
handlePromiseWithToast(
stopTrigger({
appName,
envName,
componentName: component.name,
}).unwrap,
'Stopping component',
'Failed to stop component'
)
}
onClick={onStop}
>
Stop
</Button>

<Button
disabled={isStopped || restartState.isLoading}
variant="outlined"
onClick={() =>
handlePromiseWithToast(
restartTrigger({
appName,
envName,
componentName: component.name,
}).unwrap,
'Restarting component',
'Failed to restart component'
)
}
onClick={onRestart}
>
Restart
</Button>
{restartInProgress && <CircularProgress size={32} />}
{isWorking && <CircularProgress size={32} />}
</div>
</div>
</>
Expand All @@ -88,44 +116,24 @@ export function ActiveComponentToolbar({ component, appName, envName }: Props) {

type ScaleProps = {
disabled: boolean;
appName: string;
envName: string;
componentName: string;
currentReplicas: number;
onScale: (replicas: number) => unknown;
};

function ScaleButtonPopup({
disabled,
appName,
envName,
componentName,
currentReplicas,
}: ScaleProps) {
function ScaleButtonPopup({ disabled, currentReplicas, onScale }: ScaleProps) {
const [replicas, setReplicas] = useState<number | null>(null);
const [visibleScrim, setVisibleScrim] = useState<boolean>(false);
const [scaleTrigger, scaleState] = useScaleComponentMutation();
const current = replicas ?? currentReplicas;

const onScale = handlePromiseWithToast(
async () => {
await scaleTrigger({
appName,
envName,
componentName,
replicas: current.toFixed(),
}).unwrap();
setVisibleScrim(false);
setReplicas(null);
},
'Scaling component',
'Failed to scale component'
);
const onLocalScale = async () => {
await onScale(current);
setVisibleScrim(false);
setReplicas(null);
};

return (
<div>
<Button onClick={() => setVisibleScrim(true)} disabled={disabled}>
Scale
</Button>
<Button onClick={() => setVisibleScrim(true)}>Scale</Button>

<Dialog
title={'Scale Component'}
Expand Down Expand Up @@ -153,7 +161,7 @@ function ScaleButtonPopup({

<Dialog.Actions>
<div className={'scale-component-popup-actions-wrapper'}>
<Button disabled={scaleState.isLoading} onClick={onScale}>
<Button disabled={disabled} onClick={onLocalScale}>
{current === 0 ? 'Stop component' : `Scale to ${current}`}
</Button>
<Button variant="outlined" onClick={() => setVisibleScrim(false)}>
Expand Down
6 changes: 3 additions & 3 deletions src/components/page-active-component/component-status.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Button } from '@equinor/eds-core-react';
import { useDurationInterval } from '../../effects/use-interval';
import {
type Component,
useResetScaledComponentMutation,
} from '../../store/radix-api';
import { sleep } from '../../utils/sleep';
import { Alert } from '../alert';
import { handlePromiseWithToast } from '../global-top-nav/styled-toaster';

Expand All @@ -23,14 +23,14 @@ export function ComponentStatus({
component,
}: Props) {
const [resetTrigger, resetState] = useResetScaledComponentMutation();
const startRefetch = useDurationInterval(2_000, 30_000, refetch);
const onReset = handlePromiseWithToast(async () => {
await resetTrigger({
appName,
envName,
componentName,
}).unwrap();
await sleep(1000);
await refetch();
startRefetch();
});

const isStopped = component?.status === 'Stopped';
Expand Down
35 changes: 34 additions & 1 deletion src/effects/use-interval.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef } from 'react';
import { useEffect, useRef, useState } from 'react';

export function useInterval(callback: () => void, delay?: number): void {
const savedCallback = useRef<() => void>();
Expand All @@ -17,3 +17,36 @@ export function useInterval(callback: () => void, delay?: number): void {
return () => void 0;
}, [delay]);
}

export function useDurationInterval(
intervalMs: number,
durationMs: number,
callback: () => unknown
) {
const [startAt, setStartAt] = useState<number>(0);
const interval = useRef<ReturnType<typeof setInterval>>();

const start = () => {
setStartAt(Date.now());
};

useEffect(() => {
if (startAt === 0) {
return () => void 0;
}

interval.current = setInterval(() => {
if (Date.now() > startAt + durationMs) {
setStartAt(0);
clearInterval(interval.current);
return;
}

callback();
}, intervalMs);

return () => clearInterval(interval.current);
}, [startAt, durationMs, intervalMs, callback]);

return start;
}
3 changes: 0 additions & 3 deletions src/utils/sleep.ts

This file was deleted.

0 comments on commit 55f7c9a

Please sign in to comment.