Skip to content

Commit 01de67b

Browse files
committed
feat: add control to execute forget command for an export
1 parent e4cb52f commit 01de67b

File tree

11 files changed

+193
-32
lines changed

11 files changed

+193
-32
lines changed

src/containers/Operations/Operations.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,9 @@
22
&__search {
33
width: 220px;
44
}
5+
6+
&__buttons-container {
7+
display: flex;
8+
gap: var(--g-spacing-2);
9+
}
510
}

src/containers/Operations/Operations.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {isAccessError} from '../../components/Errors/PageError/PageError';
55
import {ResponseError} from '../../components/Errors/ResponseError';
66
import {ResizeableDataTable} from '../../components/ResizeableDataTable/ResizeableDataTable';
77
import {TableWithControlsLayout} from '../../components/TableWithControlsLayout/TableWithControlsLayout';
8-
import {operationListApi} from '../../store/reducers/operationList';
8+
import {operationsApi} from '../../store/reducers/operations';
99
import {useAutoRefreshInterval} from '../../utils/hooks';
1010

1111
import {OperationsControls} from './OperationsControls';
@@ -24,7 +24,7 @@ export function Operations({database}: OperationsProps) {
2424
const {kind, searchValue, pageSize, pageToken, handleKindChange, handleSearchChange} =
2525
useOperationsQueryParams();
2626

27-
const {data, isFetching, error} = operationListApi.useGetOperationListQuery(
27+
const {data, isFetching, error, refetch} = operationsApi.useGetOperationListQuery(
2828
{database, kind, page_size: pageSize, page_token: pageToken},
2929
{
3030
pollingInterval: autoRefreshInterval,
@@ -61,7 +61,7 @@ export function Operations({database}: OperationsProps) {
6161
<TableWithControlsLayout.Table loading={isFetching} className={b('table')}>
6262
{data ? (
6363
<ResizeableDataTable
64-
columns={getColumns()}
64+
columns={getColumns({database, refreshTable: refetch})}
6565
data={filteredOperations}
6666
emptyDataMessage={i18n('title_empty')}
6767
/>

src/containers/Operations/OperationsControls.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {Select} from '@gravity-ui/uikit';
44

55
import {EntitiesCount} from '../../components/EntitiesCount';
66
import {Search} from '../../components/Search';
7-
import type {OperationKind} from '../../types/api/operationList';
7+
import type {OperationKind} from '../../types/api/operations';
88

99
import {OPERATION_KINDS} from './constants';
1010
import i18n from './i18n';

src/containers/Operations/columns.tsx

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,30 @@
11
import {duration} from '@gravity-ui/date-utils';
2+
import {Ban, CircleStop} from '@gravity-ui/icons';
23
import type {Column as DataTableColumn} from '@gravity-ui/react-data-table';
3-
import {Text} from '@gravity-ui/uikit';
4+
import {Icon, Text} from '@gravity-ui/uikit';
45

6+
import {ButtonWithConfirmDialog} from '../../components/ButtonWithConfirmDialog/ButtonWithConfirmDialog';
57
import {CellWithPopover} from '../../components/CellWithPopover/CellWithPopover';
6-
import type {TOperation} from '../../types/api/operationList';
7-
import {EStatusCode} from '../../types/api/operationList';
8+
import {operationsApi} from '../../store/reducers/operations';
9+
import type {TOperation} from '../../types/api/operations';
10+
import {EStatusCode} from '../../types/api/operations';
811
import {EMPTY_DATA_PLACEHOLDER, HOUR_IN_SECONDS, SECOND_IN_MS} from '../../utils/constants';
912
import {formatDateTime} from '../../utils/dataFormatters/dataFormatters';
1013
import {parseProtobufTimestampToMs} from '../../utils/timeParsers';
1114

1215
import {COLUMNS_NAMES, COLUMNS_TITLES} from './constants';
1316
import i18n from './i18n';
17+
import {b} from './shared';
1418

15-
export function getColumns(): DataTableColumn<TOperation>[] {
19+
import './Operations.scss';
20+
21+
export function getColumns({
22+
database,
23+
refreshTable,
24+
}: {
25+
database: string;
26+
refreshTable: VoidFunction;
27+
}): DataTableColumn<TOperation>[] {
1628
return [
1729
{
1830
name: COLUMNS_NAMES.ID,
@@ -114,5 +126,75 @@ export function getColumns(): DataTableColumn<TOperation>[] {
114126
return Date.now() - createTime;
115127
},
116128
},
129+
{
130+
name: 'Actions',
131+
sortable: false,
132+
resizeable: false,
133+
header: '',
134+
render: ({row}) => {
135+
return (
136+
<OperationsActions
137+
operation={row}
138+
database={database}
139+
refreshTable={refreshTable}
140+
/>
141+
);
142+
},
143+
},
117144
];
118145
}
146+
147+
interface OperationsActionsProps {
148+
operation: TOperation;
149+
database: string;
150+
refreshTable: VoidFunction;
151+
}
152+
153+
function OperationsActions({operation, database, refreshTable}: OperationsActionsProps) {
154+
const [cancelOperation, {isLoading: isLoadingCancel}] =
155+
operationsApi.useCancelOperationMutation();
156+
const [forgetOperation, {isLoading: isForgetLoading}] =
157+
operationsApi.useForgetOperationMutation();
158+
159+
const id = operation.id;
160+
if (!id) {
161+
return null;
162+
}
163+
164+
return (
165+
<div className={b('buttons-container')}>
166+
<ButtonWithConfirmDialog
167+
buttonView="outlined"
168+
dialogHeader={i18n('header_forget')}
169+
dialogText={i18n('text_forget')}
170+
onConfirmAction={() =>
171+
forgetOperation({id, database})
172+
.unwrap()
173+
.then(() => refreshTable())
174+
}
175+
buttonDisabled={isLoadingCancel}
176+
popoverContent={i18n('header_forget')}
177+
popoverDisabled={false}
178+
withPopover
179+
>
180+
<Icon data={Ban} />
181+
</ButtonWithConfirmDialog>
182+
<ButtonWithConfirmDialog
183+
buttonView="outlined"
184+
dialogHeader={i18n('header_cancel')}
185+
dialogText={i18n('text_cancel')}
186+
onConfirmAction={() =>
187+
cancelOperation({id, database})
188+
.unwrap()
189+
.then(() => refreshTable())
190+
}
191+
buttonDisabled={isForgetLoading}
192+
popoverContent={i18n('header_cancel')}
193+
popoverDisabled={false}
194+
withPopover
195+
>
196+
<Icon data={CircleStop} />
197+
</ButtonWithConfirmDialog>
198+
</div>
199+
);
200+
}

src/containers/Operations/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type {OperationKind} from '../../types/api/operationList';
1+
import type {OperationKind} from '../../types/api/operations';
22

33
import i18n from './i18n';
44

src/containers/Operations/i18n/en.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,10 @@
1313
"column_createTime": "Create Time",
1414
"column_endTime": "End Time",
1515
"column_duration": "Duration",
16-
"label_duration-ongoing": "{{value}} (ongoing)"
16+
"label_duration-ongoing": "{{value}} (ongoing)",
17+
18+
"header_cancel": "Cancel operation",
19+
"header_forget": "Forget operation",
20+
"text_cancel": "The operation will be cancelled. Do you want to proceed?",
21+
"text_forget": "The operation will be forgotten. Do you want to proceed?"
1722
}

src/containers/Operations/useOperationsQueryParams.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {NumberParam, StringParam, useQueryParams} from 'use-query-params';
22
import {z} from 'zod';
33

4-
import type {OperationKind} from '../../types/api/operationList';
4+
import type {OperationKind} from '../../types/api/operations';
55

66
const operationKindSchema = z.enum(['ss/backgrounds', 'export', 'buildindex']).catch('buildindex');
77

src/services/api.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ import type {ModifyDiskResponse} from '../types/api/modifyDisk';
2323
import type {TNetInfo} from '../types/api/netInfo';
2424
import type {NodesRequestParams, TNodesInfo} from '../types/api/nodes';
2525
import type {TEvNodesInfo} from '../types/api/nodesList';
26-
import type {OperationListRequestParams, TOperationList} from '../types/api/operationList';
26+
import type {
27+
OperationCancelRequestParams,
28+
OperationForgetRequestParams,
29+
OperationListRequestParams,
30+
TOperationList,
31+
} from '../types/api/operations';
2732
import type {EDecommitStatus, TEvPDiskStateResponse, TPDiskInfoResponse} from '../types/api/pdisk';
2833
import type {
2934
Actions,
@@ -886,6 +891,36 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
886891
);
887892
}
888893

894+
cancelOperation(
895+
params: OperationCancelRequestParams,
896+
{concurrentId, signal}: AxiosOptions = {},
897+
) {
898+
return this.post<TOperationList>(
899+
this.getPath('/operation/cancel'),
900+
{},
901+
{...params},
902+
{
903+
concurrentId,
904+
requestConfig: {signal},
905+
},
906+
);
907+
}
908+
909+
forgetOperation(
910+
params: OperationForgetRequestParams,
911+
{concurrentId, signal}: AxiosOptions = {},
912+
) {
913+
return this.post<TOperationList>(
914+
this.getPath('/operation/forget'),
915+
{},
916+
{...params},
917+
{
918+
concurrentId,
919+
requestConfig: {signal},
920+
},
921+
);
922+
}
923+
889924
getClusterBaseInfo(
890925
_clusterName: string,
891926
_opts: AxiosOptions = {},

src/store/reducers/operationList.ts

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/store/reducers/operations.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type {
2+
OperationCancelRequestParams,
3+
OperationForgetRequestParams,
4+
OperationListRequestParams,
5+
} from '../../types/api/operations';
6+
7+
import {api} from './api';
8+
9+
export const operationsApi = api.injectEndpoints({
10+
endpoints: (build) => ({
11+
getOperationList: build.query({
12+
queryFn: async (params: OperationListRequestParams, {signal}) => {
13+
try {
14+
const data = await window.api.getOperationList(params, {signal});
15+
return {data};
16+
} catch (error) {
17+
return {error};
18+
}
19+
},
20+
providesTags: ['All'],
21+
}),
22+
cancelOperation: build.mutation({
23+
queryFn: async (params: OperationCancelRequestParams, {signal}) => {
24+
try {
25+
const data = await window.api.cancelOperation(params, {signal});
26+
return {data};
27+
} catch (error) {
28+
return {error};
29+
}
30+
},
31+
}),
32+
forgetOperation: build.mutation({
33+
queryFn: async (params: OperationForgetRequestParams, {signal}) => {
34+
try {
35+
const data = await window.api.forgetOperation(params, {signal});
36+
return {data};
37+
} catch (error) {
38+
return {error};
39+
}
40+
},
41+
}),
42+
}),
43+
overrideExisting: 'throw',
44+
});

0 commit comments

Comments
 (0)