E2C: Create Snapshot frontend (#89901)

* First pass at using new async apis

* async api tweaks

* clean up async api usage

* Update public/app/features/migrate-to-cloud/onprem/Page.tsx

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>

* Update public/app/features/migrate-to-cloud/onprem/Page.tsx

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>

* fix syntax

---------

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
This commit is contained in:
Josh Hunt
2024-07-03 11:42:00 +01:00
committed by GitHub
parent bfe77ab530
commit 7448f22f91
6 changed files with 237 additions and 93 deletions

View File

@@ -11,24 +11,48 @@ const injectedRtkApi = api.injectEndpoints({
body: queryArg.cloudMigrationSessionRequestDto, body: queryArg.cloudMigrationSessionRequestDto,
}), }),
}), }),
getCloudMigrationRun: build.query<GetCloudMigrationRunApiResponse, GetCloudMigrationRunApiArg>({
query: (queryArg) => ({ url: `/cloudmigration/migration/run/${queryArg.runUid}` }),
}),
deleteSession: build.mutation<DeleteSessionApiResponse, DeleteSessionApiArg>({ deleteSession: build.mutation<DeleteSessionApiResponse, DeleteSessionApiArg>({
query: (queryArg) => ({ url: `/cloudmigration/migration/${queryArg.uid}`, method: 'DELETE' }), query: (queryArg) => ({ url: `/cloudmigration/migration/${queryArg.uid}`, method: 'DELETE' }),
}), }),
getSession: build.query<GetSessionApiResponse, GetSessionApiArg>({ getSession: build.query<GetSessionApiResponse, GetSessionApiArg>({
query: (queryArg) => ({ url: `/cloudmigration/migration/${queryArg.uid}` }), query: (queryArg) => ({ url: `/cloudmigration/migration/${queryArg.uid}` }),
}), }),
getCloudMigrationRunList: build.query<GetCloudMigrationRunListApiResponse, GetCloudMigrationRunListApiArg>({ createSnapshot: build.mutation<CreateSnapshotApiResponse, CreateSnapshotApiArg>({
query: (queryArg) => ({ url: `/cloudmigration/migration/${queryArg.uid}/run` }), query: (queryArg) => ({ url: `/cloudmigration/migration/${queryArg.uid}/snapshot`, method: 'POST' }),
}), }),
runCloudMigration: build.mutation<RunCloudMigrationApiResponse, RunCloudMigrationApiArg>({ getSnapshot: build.query<GetSnapshotApiResponse, GetSnapshotApiArg>({
query: (queryArg) => ({ url: `/cloudmigration/migration/${queryArg.uid}/run`, method: 'POST' }), query: (queryArg) => ({
url: `/cloudmigration/migration/${queryArg.uid}/snapshot/${queryArg.snapshotUid}`,
params: { resultPage: queryArg.resultPage, resultLimit: queryArg.resultLimit },
}),
}),
cancelSnapshot: build.mutation<CancelSnapshotApiResponse, CancelSnapshotApiArg>({
query: (queryArg) => ({
url: `/cloudmigration/migration/${queryArg.uid}/snapshot/${queryArg.snapshotUid}/cancel`,
method: 'POST',
}),
}),
uploadSnapshot: build.mutation<UploadSnapshotApiResponse, UploadSnapshotApiArg>({
query: (queryArg) => ({
url: `/cloudmigration/migration/${queryArg.uid}/snapshot/${queryArg.snapshotUid}/upload`,
method: 'POST',
}),
}),
getShapshotList: build.query<GetShapshotListApiResponse, GetShapshotListApiArg>({
query: (queryArg) => ({
url: `/cloudmigration/migration/${queryArg.uid}/snapshots`,
params: { page: queryArg.page, limit: queryArg.limit },
}),
}),
getCloudMigrationToken: build.query<GetCloudMigrationTokenApiResponse, GetCloudMigrationTokenApiArg>({
query: () => ({ url: `/cloudmigration/token` }),
}), }),
createCloudMigrationToken: build.mutation<CreateCloudMigrationTokenApiResponse, CreateCloudMigrationTokenApiArg>({ createCloudMigrationToken: build.mutation<CreateCloudMigrationTokenApiResponse, CreateCloudMigrationTokenApiArg>({
query: () => ({ url: `/cloudmigration/token`, method: 'POST' }), query: () => ({ url: `/cloudmigration/token`, method: 'POST' }),
}), }),
deleteCloudMigrationToken: build.mutation<DeleteCloudMigrationTokenApiResponse, DeleteCloudMigrationTokenApiArg>({
query: (queryArg) => ({ url: `/cloudmigration/token/${queryArg.uid}`, method: 'DELETE' }),
}),
getDashboardByUid: build.query<GetDashboardByUidApiResponse, GetDashboardByUidApiArg>({ getDashboardByUid: build.query<GetDashboardByUidApiResponse, GetDashboardByUidApiArg>({
query: (queryArg) => ({ url: `/dashboards/uid/${queryArg.uid}` }), query: (queryArg) => ({ url: `/dashboards/uid/${queryArg.uid}` }),
}), }),
@@ -42,11 +66,6 @@ export type CreateSessionApiResponse = /** status 200 (empty) */ CloudMigrationS
export type CreateSessionApiArg = { export type CreateSessionApiArg = {
cloudMigrationSessionRequestDto: CloudMigrationSessionRequestDto; cloudMigrationSessionRequestDto: CloudMigrationSessionRequestDto;
}; };
export type GetCloudMigrationRunApiResponse = /** status 200 (empty) */ MigrateDataResponseDto;
export type GetCloudMigrationRunApiArg = {
/** RunUID of a migration run */
runUid: string;
};
export type DeleteSessionApiResponse = unknown; export type DeleteSessionApiResponse = unknown;
export type DeleteSessionApiArg = { export type DeleteSessionApiArg = {
/** UID of a migration session */ /** UID of a migration session */
@@ -57,18 +76,54 @@ export type GetSessionApiArg = {
/** UID of a migration session */ /** UID of a migration session */
uid: string; uid: string;
}; };
export type GetCloudMigrationRunListApiResponse = /** status 200 (empty) */ SnapshotListDto; export type CreateSnapshotApiResponse = /** status 200 (empty) */ CreateSnapshotResponseDto;
export type GetCloudMigrationRunListApiArg = { export type CreateSnapshotApiArg = {
/** UID of a migration */ /** UID of a session */
uid: string; uid: string;
}; };
export type RunCloudMigrationApiResponse = /** status 200 (empty) */ MigrateDataResponseDto; export type GetSnapshotApiResponse = /** status 200 (empty) */ GetSnapshotResponseDto;
export type RunCloudMigrationApiArg = { export type GetSnapshotApiArg = {
/** UID of a migration */ /** ResultPage is used for pagination with ResultLimit */
resultPage?: number;
/** Max limit for snapshot results returned. */
resultLimit?: number;
/** Session UID of a session */
uid: string;
/** UID of a snapshot */
snapshotUid: string;
};
export type CancelSnapshotApiResponse = /** status 200 (empty) */ void;
export type CancelSnapshotApiArg = {
/** Session UID of a session */
uid: string;
/** UID of a snapshot */
snapshotUid: string;
};
export type UploadSnapshotApiResponse = /** status 200 (empty) */ void;
export type UploadSnapshotApiArg = {
/** Session UID of a session */
uid: string;
/** UID of a snapshot */
snapshotUid: string;
};
export type GetShapshotListApiResponse = /** status 200 (empty) */ SnapshotListResponseDto;
export type GetShapshotListApiArg = {
/** Page is used for pagination with limit */
page?: number;
/** Max limit for results returned. */
limit?: number;
/** Session UID of a session */
uid: string; uid: string;
}; };
export type GetCloudMigrationTokenApiResponse = /** status 200 (empty) */ GetAccessTokenResponseDto;
export type GetCloudMigrationTokenApiArg = void;
export type CreateCloudMigrationTokenApiResponse = /** status 200 (empty) */ CreateAccessTokenResponseDto; export type CreateCloudMigrationTokenApiResponse = /** status 200 (empty) */ CreateAccessTokenResponseDto;
export type CreateCloudMigrationTokenApiArg = void; export type CreateCloudMigrationTokenApiArg = void;
export type DeleteCloudMigrationTokenApiResponse = /** status 204 (empty) */ void;
export type DeleteCloudMigrationTokenApiArg = {
/** UID of a cloud migration token */
uid: string;
};
export type GetDashboardByUidApiResponse = /** status 200 (empty) */ DashboardFullWithMeta; export type GetDashboardByUidApiResponse = /** status 200 (empty) */ DashboardFullWithMeta;
export type GetDashboardByUidApiArg = { export type GetDashboardByUidApiArg = {
uid: string; uid: string;
@@ -95,21 +150,58 @@ export type ErrorResponseBody = {
export type CloudMigrationSessionRequestDto = { export type CloudMigrationSessionRequestDto = {
authToken?: string; authToken?: string;
}; };
export type CreateSnapshotResponseDto = {
uid?: string;
};
export type MigrateDataResponseItemDto = { export type MigrateDataResponseItemDto = {
error?: string; error?: string;
refId: string; refId: string;
status: 'OK' | 'ERROR'; status: 'OK' | 'ERROR' | 'PENDING' | 'UNKNOWN';
type: 'DASHBOARD' | 'DATASOURCE' | 'FOLDER'; type: 'DASHBOARD' | 'DATASOURCE' | 'FOLDER';
}; };
export type MigrateDataResponseDto = { export type GetSnapshotResponseDto = {
items?: MigrateDataResponseItemDto[]; created?: string;
finished?: string;
results?: MigrateDataResponseItemDto[];
sessionUid?: string;
status?:
| 'INITIALIZING'
| 'CREATING'
| 'PENDING_UPLOAD'
| 'UPLOADING'
| 'PENDING_PROCESSING'
| 'PROCESSING'
| 'FINISHED'
| 'ERROR'
| 'UNKNOWN';
uid?: string; uid?: string;
}; };
export type MigrateDataResponseListDto = { export type SnapshotDto = {
created?: string;
finished?: string;
sessionUid?: string;
status?:
| 'INITIALIZING'
| 'CREATING'
| 'PENDING_UPLOAD'
| 'UPLOADING'
| 'PENDING_PROCESSING'
| 'PROCESSING'
| 'FINISHED'
| 'ERROR'
| 'UNKNOWN';
uid?: string; uid?: string;
}; };
export type SnapshotListDto = { export type SnapshotListResponseDto = {
runs?: MigrateDataResponseListDto[]; snapshots?: SnapshotDto[];
};
export type GetAccessTokenResponseDto = {
createdAt?: string;
displayName?: string;
expiresAt?: string;
firstUsedAt?: string;
id?: string;
lastUsedAt?: string;
}; };
export type CreateAccessTokenResponseDto = { export type CreateAccessTokenResponseDto = {
token?: string; token?: string;
@@ -160,11 +252,15 @@ export type DashboardFullWithMeta = {
export const { export const {
useGetSessionListQuery, useGetSessionListQuery,
useCreateSessionMutation, useCreateSessionMutation,
useGetCloudMigrationRunQuery,
useDeleteSessionMutation, useDeleteSessionMutation,
useGetSessionQuery, useGetSessionQuery,
useGetCloudMigrationRunListQuery, useCreateSnapshotMutation,
useRunCloudMigrationMutation, useGetSnapshotQuery,
useCancelSnapshotMutation,
useUploadSnapshotMutation,
useGetShapshotListQuery,
useGetCloudMigrationTokenQuery,
useCreateCloudMigrationTokenMutation, useCreateCloudMigrationTokenMutation,
useDeleteCloudMigrationTokenMutation,
useGetDashboardByUidQuery, useGetDashboardByUidQuery,
} = injectedRtkApi; } = injectedRtkApi;

View File

@@ -4,42 +4,45 @@ import { BaseQueryFn, EndpointDefinition } from '@reduxjs/toolkit/dist/query';
import { generatedAPI } from './endpoints.gen'; import { generatedAPI } from './endpoints.gen';
export const cloudMigrationAPI = generatedAPI.enhanceEndpoints({ export const cloudMigrationAPI = generatedAPI.enhanceEndpoints({
addTagTypes: ['cloud-migration-config', 'cloud-migration-run', 'cloud-migration-run-list'], addTagTypes: ['cloud-migration-session', 'cloud-migration-snapshot'],
endpoints: { endpoints: {
// Cloud-side - create token // Cloud-side - create token
createCloudMigrationToken: suppressErrorsOnQuery, createCloudMigrationToken: suppressErrorsOnQuery,
// List Cloud Configs // List Cloud Configs
getSessionList: { getSessionList: {
providesTags: ['cloud-migration-config'] /* should this be a -list? */, providesTags: ['cloud-migration-session'] /* should this be a -list? */,
}, },
// Create Cloud Config // Create Cloud Config
createSession(endpoint) { createSession(endpoint) {
suppressErrorsOnQuery(endpoint); suppressErrorsOnQuery(endpoint);
endpoint.invalidatesTags = ['cloud-migration-config']; endpoint.invalidatesTags = ['cloud-migration-session'];
}, },
// Get one Cloud Config // Get one Cloud Config
getSession: { getSession: {
providesTags: ['cloud-migration-config'], providesTags: ['cloud-migration-session'],
}, },
// Delete one Cloud Config // Delete one Cloud Config
deleteSession: { deleteSession: {
invalidatesTags: ['cloud-migration-config'], invalidatesTags: ['cloud-migration-session', 'cloud-migration-snapshot'],
}, },
getCloudMigrationRunList: { // Snapshot management
providesTags: ['cloud-migration-run-list'], getSnapshot: {
providesTags: ['cloud-migration-snapshot'],
}, },
getShapshotList: {
getCloudMigrationRun: { providesTags: ['cloud-migration-snapshot'],
providesTags: ['cloud-migration-run'],
}, },
createSnapshot: {
runCloudMigration: { invalidatesTags: ['cloud-migration-snapshot'],
invalidatesTags: ['cloud-migration-run-list'], },
uploadSnapshot: {
invalidatesTags: ['cloud-migration-snapshot'],
}, },
getDashboardByUid: suppressErrorsOnQuery, getDashboardByUid: suppressErrorsOnQuery,

View File

@@ -1,15 +1,17 @@
import { skipToken } from '@reduxjs/toolkit/query/react'; import { skipToken } from '@reduxjs/toolkit/query/react';
import { useCallback, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { Alert, Box, Button, Stack } from '@grafana/ui'; import { Alert, Box, Button, Stack } from '@grafana/ui';
import { Trans, t } from 'app/core/internationalization'; import { Trans, t } from 'app/core/internationalization';
import { import {
SnapshotDto,
useCreateSnapshotMutation,
useDeleteSessionMutation, useDeleteSessionMutation,
useGetCloudMigrationRunListQuery,
useGetCloudMigrationRunQuery,
useGetSessionListQuery, useGetSessionListQuery,
useRunCloudMigrationMutation, useGetShapshotListQuery,
useGetSnapshotQuery,
useUploadSnapshotMutation,
} from '../api'; } from '../api';
import { DisconnectModal } from './DisconnectModal'; import { DisconnectModal } from './DisconnectModal';
@@ -32,7 +34,7 @@ import { ResourcesTable } from './ResourcesTable';
* 2. call GetCloudMigrationRun with the ID from first step to list the result of that migration * 2. call GetCloudMigrationRun with the ID from first step to list the result of that migration
*/ */
function useGetLatestMigrationDestination() { function useGetLatestSession() {
const result = useGetSessionListQuery(); const result = useGetSessionListQuery();
const latestMigration = result.data?.sessions?.at(-1); const latestMigration = result.data?.sessions?.at(-1);
@@ -42,64 +44,88 @@ function useGetLatestMigrationDestination() {
}; };
} }
function useGetLatestMigrationRun(migrationUid?: string) { const SHOULD_POLL_STATUSES: Array<SnapshotDto['status']> = [
const listResult = useGetCloudMigrationRunListQuery(migrationUid ? { uid: migrationUid } : skipToken); 'INITIALIZING',
const latestMigrationRun = listResult.data?.runs?.at(-1); 'CREATING',
'UPLOADING',
'PENDING_PROCESSING',
'PROCESSING',
];
const runResult = useGetCloudMigrationRunQuery( const STATUS_POLL_INTERVAL = 5 * 1000;
latestMigrationRun?.uid && migrationUid ? { runUid: latestMigrationRun.uid } : skipToken
); function useGetLatestSnapshot(sessionUid?: string) {
const [shouldPoll, setShouldPoll] = useState(false);
const listResult = useGetShapshotListQuery(sessionUid ? { uid: sessionUid } : skipToken);
const lastItem = listResult.data?.snapshots?.at(-1); // TODO: account for pagination and ensure we're truely getting the last one
const getSnapshotQueryArgs = sessionUid && lastItem?.uid ? { uid: sessionUid, snapshotUid: lastItem.uid } : skipToken;
const snapshotResult = useGetSnapshotQuery(getSnapshotQueryArgs, {
pollingInterval: shouldPoll ? STATUS_POLL_INTERVAL : 0,
skipPollingIfUnfocused: true,
});
useEffect(() => {
const shouldPoll = SHOULD_POLL_STATUSES.includes(snapshotResult.data?.status);
setShouldPoll(shouldPoll);
}, [snapshotResult?.data?.status]);
return { return {
...runResult, ...snapshotResult,
data: runResult.data, error: listResult.error || snapshotResult.error,
error: listResult.error || runResult.error, // isSuccess and isUninitialised should always be from snapshotResult
// as only the 'final' values from those are important
isError: listResult.isError || runResult.isError, isError: listResult.isError || snapshotResult.isError,
isLoading: listResult.isLoading || runResult.isLoading, isLoading: listResult.isLoading || snapshotResult.isLoading,
isFetching: listResult.isFetching || runResult.isFetching, isFetching: listResult.isFetching || snapshotResult.isFetching,
}; };
} }
export const Page = () => { export const Page = () => {
const [disconnectModalOpen, setDisconnectModalOpen] = useState(false); const [disconnectModalOpen, setDisconnectModalOpen] = useState(false);
const migrationDestination = useGetLatestMigrationDestination(); const session = useGetLatestSession();
const lastMigrationRun = useGetLatestMigrationRun(migrationDestination.data?.uid); const snapshot = useGetLatestSnapshot(session.data?.uid);
const [performRunMigration, runMigrationResult] = useRunCloudMigrationMutation(); const [performCreateSnapshot, createSnapshotResult] = useCreateSnapshotMutation();
const [performUploadSnapshot, uploadSnapshotResult] = useUploadSnapshotMutation();
const [performDisconnect, disconnectResult] = useDeleteSessionMutation(); const [performDisconnect, disconnectResult] = useDeleteSessionMutation();
const sessionUid = session.data?.uid;
const snapshotUid = snapshot.data?.uid;
const migrationMeta = session.data;
const isInitialLoading = session.isLoading;
// isBusy is not a loading state, but indicates that the system is doing *something* // isBusy is not a loading state, but indicates that the system is doing *something*
// and all buttons should be disabled // and all buttons should be disabled
const isBusy = const isBusy =
runMigrationResult.isLoading || createSnapshotResult.isLoading ||
migrationDestination.isFetching || uploadSnapshotResult.isLoading ||
lastMigrationRun.isFetching || session.isLoading ||
snapshot.isLoading ||
disconnectResult.isLoading; disconnectResult.isLoading;
const resources = lastMigrationRun.data?.items; const resources = snapshot.data?.results;
const migrationDestUID = migrationDestination.data?.uid;
const handleDisconnect = useCallback(async () => { const handleDisconnect = useCallback(async () => {
if (!migrationDestUID) { if (sessionUid) {
return; performDisconnect({ uid: sessionUid });
} }
}, [performDisconnect, sessionUid]);
const resp = await performDisconnect({ uid: migrationDestUID }); const handleCreateSnapshot = useCallback(() => {
if (!('error' in resp)) { if (sessionUid) {
setDisconnectModalOpen(false); performCreateSnapshot({ uid: sessionUid });
} }
}, [migrationDestUID, performDisconnect]); }, [performCreateSnapshot, sessionUid]);
const handleStartMigration = useCallback(() => { const handleUploadSnapshot = useCallback(() => {
if (migrationDestination.data?.uid) { if (sessionUid && snapshotUid) {
performRunMigration({ uid: migrationDestination.data?.uid }); performUploadSnapshot({ uid: sessionUid, snapshotUid: snapshotUid });
} }
}, [performRunMigration, migrationDestination]); }, [performUploadSnapshot, sessionUid, snapshotUid]);
const migrationMeta = migrationDestination.data;
const isInitialLoading = migrationDestination.isLoading;
if (isInitialLoading) { if (isInitialLoading) {
// TODO: better loading state // TODO: better loading state
@@ -111,7 +137,7 @@ export const Page = () => {
return ( return (
<> <>
<Stack direction="column" gap={4}> <Stack direction="column" gap={4}>
{runMigrationResult.isError && ( {createSnapshotResult.isError && (
<Alert <Alert
severity="error" severity="error"
title={t( title={t(
@@ -164,12 +190,22 @@ export const Page = () => {
} }
/> />
<MigrationInfo title="Status" value={snapshot?.data?.status ?? 'no snapshot yet'} />
<Button <Button
disabled={isBusy} disabled={isBusy || Boolean(snapshot.data)}
onClick={handleStartMigration} onClick={handleCreateSnapshot}
icon={runMigrationResult.isLoading ? 'spinner' : undefined} icon={createSnapshotResult.isLoading ? 'spinner' : undefined}
> >
<Trans i18nKey="migrate-to-cloud.summary.start-migration">Upload everything</Trans> <Trans i18nKey="migrate-to-cloud.summary.start-migration">Build snapshot</Trans>
</Button>
<Button
disabled={isBusy || !(snapshot.data?.status === 'PENDING_UPLOAD')}
onClick={handleUploadSnapshot}
icon={createSnapshotResult.isLoading ? 'spinner' : undefined}
>
<Trans i18nKey="migrate-to-cloud.summary.upload-migration">Upload & migrate snapshot</Trans>
</Button> </Button>
</Box> </Box>
)} )}

View File

@@ -968,8 +968,9 @@
"disconnect-error-title": "There was an error disconnecting", "disconnect-error-title": "There was an error disconnecting",
"run-migration-error-description": "See the Grafana server logs for more details", "run-migration-error-description": "See the Grafana server logs for more details",
"run-migration-error-title": "There was an error migrating your resources", "run-migration-error-title": "There was an error migrating your resources",
"start-migration": "Upload everything", "start-migration": "Build snapshot",
"target-stack-title": "Uploading to" "target-stack-title": "Uploading to",
"upload-migration": "Upload & migrate snapshot"
}, },
"token-status": { "token-status": {
"active": "Token created and active", "active": "Token created and active",

View File

@@ -968,8 +968,9 @@
"disconnect-error-title": "Ŧĥęřę ŵäş äʼn ęřřőř đįşčőʼnʼnęčŧįʼnģ", "disconnect-error-title": "Ŧĥęřę ŵäş äʼn ęřřőř đįşčőʼnʼnęčŧįʼnģ",
"run-migration-error-description": "Ŝęę ŧĥę Ğřäƒäʼnä şęřvęř ľőģş ƒőř mőřę đęŧäįľş", "run-migration-error-description": "Ŝęę ŧĥę Ğřäƒäʼnä şęřvęř ľőģş ƒőř mőřę đęŧäįľş",
"run-migration-error-title": "Ŧĥęřę ŵäş äʼn ęřřőř mįģřäŧįʼnģ yőūř řęşőūřčęş", "run-migration-error-title": "Ŧĥęřę ŵäş äʼn ęřřőř mįģřäŧįʼnģ yőūř řęşőūřčęş",
"start-migration": "Ůpľőäđ ęvęřyŧĥįʼnģ", "start-migration": "ßūįľđ şʼnäpşĥőŧ",
"target-stack-title": "Ůpľőäđįʼnģ ŧő" "target-stack-title": "Ůpľőäđįʼnģ ŧő",
"upload-migration": "Ůpľőäđ & mįģřäŧę şʼnäpşĥőŧ"
}, },
"token-status": { "token-status": {
"active": "Ŧőĸęʼn čřęäŧęđ äʼnđ äčŧįvę", "active": "Ŧőĸęʼn čřęäŧęđ äʼnđ äčŧįvę",

View File

@@ -12,14 +12,21 @@ const config: ConfigFile = {
apiFile: '../public/app/features/migrate-to-cloud/api/baseAPI.ts', apiFile: '../public/app/features/migrate-to-cloud/api/baseAPI.ts',
apiImport: 'baseAPI', apiImport: 'baseAPI',
filterEndpoints: [ filterEndpoints: [
'createCloudMigrationToken',
'getSessionList', 'getSessionList',
'getSession', 'getSession',
'createSession',
'deleteSession', 'deleteSession',
'runCloudMigration', 'createSession',
'getCloudMigrationRun',
'getCloudMigrationRunList', 'getShapshotList',
'getSnapshot',
'uploadSnapshot',
'createSnapshot',
'cancelSnapshot',
'createCloudMigrationToken',
'deleteCloudMigrationToken',
'getCloudMigrationToken',
'getDashboardByUid', 'getDashboardByUid',
], ],
}, },