Skip to content

Commit

Permalink
feat(PDiskPage): add force restart
Browse files Browse the repository at this point in the history
  • Loading branch information
artemmufazalov committed Jul 29, 2024
1 parent f4af922 commit 219dcee
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 30 deletions.
19 changes: 15 additions & 4 deletions src/components/ButtonWithConfirmDialog/ButtonWithConfirmDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import {CriticalActionDialog} from '../CriticalActionDialog';

interface ButtonWithConfirmDialogProps<T, K> {
children: React.ReactNode;
onConfirmAction: () => Promise<T>;
onConfirmAction: (isRetry?: boolean) => Promise<T>;
onConfirmActionSuccess?: (() => Promise<K>) | VoidFunction;
dialogContent: string;
retryButtonText?: string;
buttonDisabled?: ButtonProps['disabled'];
buttonView?: ButtonProps['view'];
buttonClassName?: ButtonProps['className'];
Expand All @@ -24,6 +25,7 @@ export function ButtonWithConfirmDialog<T, K>({
onConfirmAction,
onConfirmActionSuccess,
dialogContent,
retryButtonText,
buttonDisabled = false,
buttonView = 'action',
buttonClassName,
Expand All @@ -34,14 +36,17 @@ export function ButtonWithConfirmDialog<T, K>({
}: ButtonWithConfirmDialogProps<T, K>) {
const [isConfirmDialogVisible, setIsConfirmDialogVisible] = React.useState(false);
const [buttonLoading, setButtonLoading] = React.useState(false);
const [withRetry, setWithRetry] = React.useState(false);

const handleConfirmAction = async () => {
const handleConfirmAction = async (isRetry?: boolean) => {
setButtonLoading(true);
await onConfirmAction();
await onConfirmAction(isRetry);
setButtonLoading(false);
};

const handleConfirmActionSuccess = async () => {
setWithRetry(false);

if (onConfirmActionSuccess) {
setButtonLoading(true);

Expand All @@ -54,7 +59,11 @@ export function ButtonWithConfirmDialog<T, K>({
}
};

const handleConfirmActionError = () => {
const handleConfirmActionError = (error: unknown) => {
const isWithRetry = Boolean(
error && typeof error === 'object' && 'retryPossible' in error && error.retryPossible,
);
setWithRetry(isWithRetry);
setButtonLoading(false);
};

Expand Down Expand Up @@ -93,6 +102,8 @@ export function ButtonWithConfirmDialog<T, K>({
<CriticalActionDialog
visible={isConfirmDialogVisible}
text={dialogContent}
withRetry={withRetry}
retryButtonText={retryButtonText}
onConfirm={handleConfirmAction}
onConfirmActionSuccess={handleConfirmActionSuccess}
onConfirmActionError={handleConfirmActionError}
Expand Down
2 changes: 2 additions & 0 deletions src/components/CriticalActionDialog/CriticalActionDialog.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@
&__body {
display: flex;
align-items: center;

padding-top: var(--g-spacing-7);
}
}
27 changes: 18 additions & 9 deletions src/components/CriticalActionDialog/CriticalActionDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,19 @@ const parseError = (error: IResponseError) => {
interface CriticalActionDialogProps<T> {
visible: boolean;
text: string;
withRetry?: boolean;
retryButtonText?: string;
onClose: VoidFunction;
onConfirm: () => Promise<T>;
onConfirm: (isRetry?: boolean) => Promise<T>;
onConfirmActionSuccess: VoidFunction;
onConfirmActionError: VoidFunction;
onConfirmActionError: (error: unknown) => void;
}

export function CriticalActionDialog<T>({
visible,
text,
withRetry,
retryButtonText,
onClose,
onConfirm,
onConfirmActionSuccess,
Expand All @@ -43,17 +47,16 @@ export function CriticalActionDialog<T>({
const [isLoading, setIsLoading] = React.useState(false);
const [error, setError] = React.useState<IResponseError>();

const onSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const onApply = async (isRetry?: boolean) => {
setIsLoading(true);

return onConfirm()
return onConfirm(isRetry)
.then(() => {
onConfirmActionSuccess();
onClose();
})
.catch((err) => {
onConfirmActionError();
onConfirmActionError(err);
setError(err);
})
.finally(() => {
Expand All @@ -75,15 +78,21 @@ export function CriticalActionDialog<T>({
<Dialog.Footer
loading={false}
preset="default"
textButtonApply={
withRetry
? retryButtonText || criticalActionDialogKeyset('button-retry')
: undefined
}
textButtonCancel={criticalActionDialogKeyset('button-close')}
onClickButtonApply={() => onApply(true)}
onClickButtonCancel={onClose}
/>
</React.Fragment>
);
}

return (
<form onSubmit={onSubmit}>
<React.Fragment>
<Dialog.Body className={b('body')}>
<span className={b('warning-icon')}>
<Icon data={TriangleExclamationFill} size={24} />
Expand All @@ -98,9 +107,9 @@ export function CriticalActionDialog<T>({
textButtonCancel={criticalActionDialogKeyset('button-cancel')}
propsButtonApply={{type: 'submit'}}
onClickButtonCancel={onClose}
onClickButtonApply={() => {}}
onClickButtonApply={() => onApply()}
/>
</form>
</React.Fragment>
);
};

Expand Down
1 change: 1 addition & 0 deletions src/components/CriticalActionDialog/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"no-rights-error": "You don't have enough rights to complete the operation",

"button-confirm": "Confirm",
"button-retry": "Retry",
"button-cancel": "Cancel",
"button-close": "Close"
}
7 changes: 4 additions & 3 deletions src/containers/PDiskPage/PDiskPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ export function PDiskPage() {
const pDiskData = pdiskDataQuery.currentData;
const {NodeHost, NodeId, NodeType, NodeDC, Severity} = pDiskData || {};

const handleRestart = async () => {
const handleRestart = async (isRetry?: boolean) => {
if (pDiskParamsDefined) {
return window.api.restartPDisk(nodeId, pDiskId).then((res) => {
return window.api.restartPDisk({nodeId, pDiskId, force: isRetry}).then((res) => {
if (res?.result === false) {
const err = {statusText: res.error};
const err = {statusText: res.error, retryPossible: res.forceRetryPossible};
throw err;
}
});
Expand Down Expand Up @@ -147,6 +147,7 @@ export function PDiskPage() {
buttonDisabled={!nodeId || !pDiskId || !isUserAllowedToMakeChanges}
buttonView="normal"
dialogContent={pDiskPageKeyset('restart-pdisk-dialog')}
retryButtonText={pDiskPageKeyset('force-restart-pdisk-button')}
withPopover
popoverContent={pDiskPageKeyset('restart-pdisk-not-allowed')}
popoverDisabled={isUserAllowedToMakeChanges}
Expand Down
1 change: 1 addition & 0 deletions src/containers/PDiskPage/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"no-slots-data": "No slots data",

"restart-pdisk-button": "Restart PDisk",
"force-restart-pdisk-button": "Restart anyway",
"restart-pdisk-dialog": "PDisk will be restarted. Do you want to proceed?",
"restart-pdisk-not-allowed": "You don't have enough rights to restart PDisk"
}
24 changes: 11 additions & 13 deletions src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import type {TEvVDiskStateResponse} from '../types/api/vdisk';
import type {TUserToken} from '../types/api/whoami';
import type {QuerySyntax} from '../types/store/query';
import {BINARY_DATA_IN_PLAIN_TEXT_DISPLAY} from '../utils/constants';
import {createPDiskDeveloperUILink} from '../utils/developerUI/developerUI';
import {prepareSortValue} from '../utils/filters';
import type {Nullable} from '../utils/typecheckers';

Expand Down Expand Up @@ -535,21 +534,20 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
},
);
}
restartPDisk(nodeId: number | string, pDiskId: number | string) {
const pDiskPath = createPDiskDeveloperUILink({
nodeId,
pDiskId,
host: this.getPath(''),
});

restartPDisk({
nodeId,
pDiskId,
force,
}: {
nodeId: number | string;
pDiskId: number | string;
force?: boolean;
}) {
return this.post<RestartPDiskResponse>(
pDiskPath,
'restartPDisk=',
this.getPath(`/pdisk/restart?node_id=${nodeId}&pdisk_id=${pDiskId}&force=${force}`),
{},
{},
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
},
requestConfig: {'axios-retry': {retries: 0}},
},
);
Expand Down
3 changes: 2 additions & 1 deletion src/types/api/restartPDisk.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export interface RestartPDiskResponse {
// true if successful, false if not
result?: boolean;
// Error message, example: "GroupId# 2181038081 ExpectedStatus# DISINTEGRATED"
// Error message
error?: string;
forceRetryPossible?: boolean;
}

0 comments on commit 219dcee

Please sign in to comment.