diff --git a/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap b/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap
index 48a27c18ea2..9b5c0a119ae 100644
--- a/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap
+++ b/public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModel.test.ts.snap
@@ -278,6 +278,7 @@ exports[`transformSceneToSaveModel Given a scene with rows Should transform back
"type": "row",
},
],
+ "preload": false,
"refresh": "",
"schemaVersion": 39,
"tags": [
@@ -545,6 +546,7 @@ exports[`transformSceneToSaveModel Given a simple scene with custom settings Sho
"type": "text",
},
],
+ "preload": false,
"refresh": "5m",
"schemaVersion": 39,
"tags": [
@@ -902,6 +904,7 @@ exports[`transformSceneToSaveModel Given a simple scene with variables Should tr
"type": "text",
},
],
+ "preload": false,
"refresh": "",
"schemaVersion": 39,
"tags": [
diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts
index 444676ca936..ea3e0c936f5 100644
--- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts
+++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts
@@ -111,7 +111,7 @@ describe('transformSaveModelToScene', () => {
};
const oldModel = new DashboardModel(dash);
- const scene = createDashboardSceneFromDashboardModel(oldModel);
+ const scene = createDashboardSceneFromDashboardModel(oldModel, dash);
const dashboardControls = scene.state.controls!;
expect(scene.state.title).toBe('test');
@@ -138,11 +138,13 @@ describe('transformSaveModelToScene', () => {
it('should apply cursor sync behavior', () => {
const dash = {
...defaultDashboard,
+ title: 'Test dashboard',
+ uid: 'test-uid',
graphTooltip: DashboardCursorSync.Crosshair,
};
const oldModel = new DashboardModel(dash);
- const scene = createDashboardSceneFromDashboardModel(oldModel);
+ const scene = createDashboardSceneFromDashboardModel(oldModel, dash);
const cursorSync = scene.state.$behaviors?.find((b) => b instanceof behaviors.CursorSync);
expect(cursorSync).toBeInstanceOf(behaviors.CursorSync);
@@ -150,8 +152,13 @@ describe('transformSaveModelToScene', () => {
});
it('should apply live now timer behavior', () => {
- const oldModel = new DashboardModel(defaultDashboard);
- const scene = createDashboardSceneFromDashboardModel(oldModel);
+ const dash = {
+ ...defaultDashboard,
+ title: 'Test dashboard',
+ uid: 'test-uid',
+ };
+ const oldModel = new DashboardModel(dash);
+ const scene = createDashboardSceneFromDashboardModel(oldModel, dash);
const liveNowTimer = scene.state.$behaviors?.find((b) => b instanceof behaviors.LiveNowTimer);
expect(liveNowTimer).toBeInstanceOf(behaviors.LiveNowTimer);
@@ -172,7 +179,7 @@ describe('transformSaveModelToScene', () => {
};
const oldModel = new DashboardModel(dash);
- const scene = createDashboardSceneFromDashboardModel(oldModel);
+ const scene = createDashboardSceneFromDashboardModel(oldModel, dash);
expect(scene.state.$variables?.state.variables).toBeDefined();
});
});
@@ -212,12 +219,14 @@ describe('transformSaveModelToScene', () => {
const dashboard = {
...defaultDashboard,
+ title: 'Test dashboard',
+ uid: 'test-uid',
panels: [row],
};
const oldModel = new DashboardModel(dashboard);
- const scene = createDashboardSceneFromDashboardModel(oldModel);
+ const scene = createDashboardSceneFromDashboardModel(oldModel, dashboard);
const body = scene.state.body as SceneGridLayout;
expect(body.state.children).toHaveLength(1);
@@ -304,12 +313,14 @@ describe('transformSaveModelToScene', () => {
const dashboard = {
...defaultDashboard,
+ title: 'Test dashboard',
+ uid: 'test-uid',
panels: [panelOutOfRow, libPanelOutOfRow, rowWithPanel, panelInRow, libPanelInRow, emptyRow],
};
const oldModel = new DashboardModel(dashboard);
- const scene = createDashboardSceneFromDashboardModel(oldModel);
+ const scene = createDashboardSceneFromDashboardModel(oldModel, dashboard);
const body = scene.state.body as SceneGridLayout;
expect(body.state.children).toHaveLength(4);
diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts
index 2d76314a1cb..1e11bc51bde 100644
--- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts
+++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts
@@ -30,7 +30,7 @@ import {
AdHocFiltersVariable,
} from '@grafana/scenes';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
-import { DashboardDTO } from 'app/types';
+import { DashboardDTO, DashboardDataDTO } from 'app/types';
import { AlertStatesDataLayer } from '../scene/AlertStatesDataLayer';
import { DashboardAnnotationsDataLayer } from '../scene/DashboardAnnotationsDataLayer';
@@ -74,7 +74,7 @@ export function transformSaveModelToScene(rsp: DashboardDTO): DashboardScene {
// Just to have migrations run
const oldModel = new DashboardModel(rsp.dashboard, rsp.meta);
- const scene = createDashboardSceneFromDashboardModel(oldModel);
+ const scene = createDashboardSceneFromDashboardModel(oldModel, rsp.dashboard);
// TODO: refactor createDashboardSceneFromDashboardModel to work on Dashboard schema model
scene.setInitialSaveModel(rsp.dashboard);
@@ -190,7 +190,7 @@ function createRowFromPanelModel(row: PanelModel, content: SceneGridItemLike[]):
});
}
-export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel) {
+export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel, dto: DashboardDataDTO) {
let variables: SceneVariableSet | undefined;
let annotationLayers: SceneDataLayerProvider[] = [];
let alertStatesLayer: AlertStatesDataLayer | undefined;
@@ -249,6 +249,7 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
const dashboardScene = new DashboardScene({
description: oldModel.description,
editable: oldModel.editable,
+ preload: dto.preload ?? false,
id: oldModel.id,
isDirty: false,
links: oldModel.links || [],
@@ -258,7 +259,7 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel)
uid: oldModel.uid,
version: oldModel.version,
body: new SceneGridLayout({
- isLazy: true,
+ isLazy: dto.preload ? false : true,
children: createSceneObjectsForPanels(oldModel.panels),
$behaviors: [trackIfEmpty],
}),
diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts
index e633b7fb0ce..d6bba6aa82f 100644
--- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts
+++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts
@@ -112,6 +112,7 @@ export function transformSceneToSaveModel(scene: DashboardScene, isSnapshot = fa
uid: state.uid,
id: state.id,
editable: state.editable,
+ preload: state.preload,
time: {
from: timeRange.from,
to: timeRange.to,
diff --git a/public/app/features/dashboard-scene/settings/GeneralSettingsEditView.tsx b/public/app/features/dashboard-scene/settings/GeneralSettingsEditView.tsx
index fafe32472c7..73b1d8cdceb 100644
--- a/public/app/features/dashboard-scene/settings/GeneralSettingsEditView.tsx
+++ b/public/app/features/dashboard-scene/settings/GeneralSettingsEditView.tsx
@@ -12,6 +12,7 @@ import {
Label,
RadioButtonGroup,
Stack,
+ Switch,
TagsInput,
TextArea,
} from '@grafana/ui';
@@ -161,6 +162,10 @@ export class GeneralSettingsEditView
this.getCursorSync()?.setState({ sync: value });
};
+ public onPreloadChange = (preload: boolean) => {
+ this._dashboard.setState({ preload });
+ };
+
public onDeleteDashboard = () => {};
static Component = ({ model }: SceneComponentProps
) => {
@@ -271,6 +276,20 @@ export class GeneralSettingsEditView
>
+
+
+ model.onPreloadChange(e.currentTarget.checked)}
+ />
+
{meta.canDelete && }
diff --git a/public/app/features/dashboard/components/HelpWizard/SupportSnapshotService.ts b/public/app/features/dashboard/components/HelpWizard/SupportSnapshotService.ts
index 0a1f9b39f2c..e9f68ad556d 100644
--- a/public/app/features/dashboard/components/HelpWizard/SupportSnapshotService.ts
+++ b/public/app/features/dashboard/components/HelpWizard/SupportSnapshotService.ts
@@ -83,7 +83,7 @@ export class SupportSnapshotService extends StateManagerBase({
- query: (queryArg) => ({ url: `/cloudmigration/migration/run/${queryArg.runUid}` }),
- }),
deleteSession: build.mutation({
query: (queryArg) => ({ url: `/cloudmigration/migration/${queryArg.uid}`, method: 'DELETE' }),
}),
getSession: build.query({
query: (queryArg) => ({ url: `/cloudmigration/migration/${queryArg.uid}` }),
}),
- getCloudMigrationRunList: build.query({
- query: (queryArg) => ({ url: `/cloudmigration/migration/${queryArg.uid}/run` }),
+ createSnapshot: build.mutation({
+ query: (queryArg) => ({ url: `/cloudmigration/migration/${queryArg.uid}/snapshot`, method: 'POST' }),
}),
- runCloudMigration: build.mutation({
- query: (queryArg) => ({ url: `/cloudmigration/migration/${queryArg.uid}/run`, method: 'POST' }),
+ getSnapshot: build.query({
+ query: (queryArg) => ({
+ url: `/cloudmigration/migration/${queryArg.uid}/snapshot/${queryArg.snapshotUid}`,
+ params: { resultPage: queryArg.resultPage, resultLimit: queryArg.resultLimit },
+ }),
+ }),
+ cancelSnapshot: build.mutation({
+ query: (queryArg) => ({
+ url: `/cloudmigration/migration/${queryArg.uid}/snapshot/${queryArg.snapshotUid}/cancel`,
+ method: 'POST',
+ }),
+ }),
+ uploadSnapshot: build.mutation({
+ query: (queryArg) => ({
+ url: `/cloudmigration/migration/${queryArg.uid}/snapshot/${queryArg.snapshotUid}/upload`,
+ method: 'POST',
+ }),
+ }),
+ getShapshotList: build.query({
+ query: (queryArg) => ({
+ url: `/cloudmigration/migration/${queryArg.uid}/snapshots`,
+ params: { page: queryArg.page, limit: queryArg.limit },
+ }),
+ }),
+ getCloudMigrationToken: build.query({
+ query: () => ({ url: `/cloudmigration/token` }),
}),
createCloudMigrationToken: build.mutation({
query: () => ({ url: `/cloudmigration/token`, method: 'POST' }),
}),
+ deleteCloudMigrationToken: build.mutation({
+ query: (queryArg) => ({ url: `/cloudmigration/token/${queryArg.uid}`, method: 'DELETE' }),
+ }),
getDashboardByUid: build.query({
query: (queryArg) => ({ url: `/dashboards/uid/${queryArg.uid}` }),
}),
@@ -42,11 +66,6 @@ export type CreateSessionApiResponse = /** status 200 (empty) */ CloudMigrationS
export type CreateSessionApiArg = {
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 DeleteSessionApiArg = {
/** UID of a migration session */
@@ -57,18 +76,54 @@ export type GetSessionApiArg = {
/** UID of a migration session */
uid: string;
};
-export type GetCloudMigrationRunListApiResponse = /** status 200 (empty) */ SnapshotListDto;
-export type GetCloudMigrationRunListApiArg = {
- /** UID of a migration */
+export type CreateSnapshotApiResponse = /** status 200 (empty) */ CreateSnapshotResponseDto;
+export type CreateSnapshotApiArg = {
+ /** UID of a session */
uid: string;
};
-export type RunCloudMigrationApiResponse = /** status 200 (empty) */ MigrateDataResponseDto;
-export type RunCloudMigrationApiArg = {
- /** UID of a migration */
+export type GetSnapshotApiResponse = /** status 200 (empty) */ GetSnapshotResponseDto;
+export type GetSnapshotApiArg = {
+ /** 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;
};
+export type GetCloudMigrationTokenApiResponse = /** status 200 (empty) */ GetAccessTokenResponseDto;
+export type GetCloudMigrationTokenApiArg = void;
export type CreateCloudMigrationTokenApiResponse = /** status 200 (empty) */ CreateAccessTokenResponseDto;
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 GetDashboardByUidApiArg = {
uid: string;
@@ -95,21 +150,58 @@ export type ErrorResponseBody = {
export type CloudMigrationSessionRequestDto = {
authToken?: string;
};
+export type CreateSnapshotResponseDto = {
+ uid?: string;
+};
export type MigrateDataResponseItemDto = {
error?: string;
refId: string;
- status: 'OK' | 'ERROR';
+ status: 'OK' | 'ERROR' | 'PENDING' | 'UNKNOWN';
type: 'DASHBOARD' | 'DATASOURCE' | 'FOLDER';
};
-export type MigrateDataResponseDto = {
- items?: MigrateDataResponseItemDto[];
+export type GetSnapshotResponseDto = {
+ created?: string;
+ finished?: string;
+ results?: MigrateDataResponseItemDto[];
+ sessionUid?: string;
+ status?:
+ | 'INITIALIZING'
+ | 'CREATING'
+ | 'PENDING_UPLOAD'
+ | 'UPLOADING'
+ | 'PENDING_PROCESSING'
+ | 'PROCESSING'
+ | 'FINISHED'
+ | 'ERROR'
+ | 'UNKNOWN';
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;
};
-export type SnapshotListDto = {
- runs?: MigrateDataResponseListDto[];
+export type SnapshotListResponseDto = {
+ snapshots?: SnapshotDto[];
+};
+export type GetAccessTokenResponseDto = {
+ createdAt?: string;
+ displayName?: string;
+ expiresAt?: string;
+ firstUsedAt?: string;
+ id?: string;
+ lastUsedAt?: string;
};
export type CreateAccessTokenResponseDto = {
token?: string;
@@ -160,11 +252,15 @@ export type DashboardFullWithMeta = {
export const {
useGetSessionListQuery,
useCreateSessionMutation,
- useGetCloudMigrationRunQuery,
useDeleteSessionMutation,
useGetSessionQuery,
- useGetCloudMigrationRunListQuery,
- useRunCloudMigrationMutation,
+ useCreateSnapshotMutation,
+ useGetSnapshotQuery,
+ useCancelSnapshotMutation,
+ useUploadSnapshotMutation,
+ useGetShapshotListQuery,
+ useGetCloudMigrationTokenQuery,
useCreateCloudMigrationTokenMutation,
+ useDeleteCloudMigrationTokenMutation,
useGetDashboardByUidQuery,
} = injectedRtkApi;
diff --git a/public/app/features/migrate-to-cloud/api/index.ts b/public/app/features/migrate-to-cloud/api/index.ts
index 696a5a8505b..6b05a867aef 100644
--- a/public/app/features/migrate-to-cloud/api/index.ts
+++ b/public/app/features/migrate-to-cloud/api/index.ts
@@ -4,42 +4,45 @@ import { BaseQueryFn, EndpointDefinition } from '@reduxjs/toolkit/dist/query';
import { generatedAPI } from './endpoints.gen';
export const cloudMigrationAPI = generatedAPI.enhanceEndpoints({
- addTagTypes: ['cloud-migration-config', 'cloud-migration-run', 'cloud-migration-run-list'],
+ addTagTypes: ['cloud-migration-session', 'cloud-migration-snapshot'],
+
endpoints: {
// Cloud-side - create token
createCloudMigrationToken: suppressErrorsOnQuery,
// List Cloud Configs
getSessionList: {
- providesTags: ['cloud-migration-config'] /* should this be a -list? */,
+ providesTags: ['cloud-migration-session'] /* should this be a -list? */,
},
// Create Cloud Config
createSession(endpoint) {
suppressErrorsOnQuery(endpoint);
- endpoint.invalidatesTags = ['cloud-migration-config'];
+ endpoint.invalidatesTags = ['cloud-migration-session'];
},
// Get one Cloud Config
getSession: {
- providesTags: ['cloud-migration-config'],
+ providesTags: ['cloud-migration-session'],
},
// Delete one Cloud Config
deleteSession: {
- invalidatesTags: ['cloud-migration-config'],
+ invalidatesTags: ['cloud-migration-session', 'cloud-migration-snapshot'],
},
- getCloudMigrationRunList: {
- providesTags: ['cloud-migration-run-list'],
+ // Snapshot management
+ getSnapshot: {
+ providesTags: ['cloud-migration-snapshot'],
},
-
- getCloudMigrationRun: {
- providesTags: ['cloud-migration-run'],
+ getShapshotList: {
+ providesTags: ['cloud-migration-snapshot'],
},
-
- runCloudMigration: {
- invalidatesTags: ['cloud-migration-run-list'],
+ createSnapshot: {
+ invalidatesTags: ['cloud-migration-snapshot'],
+ },
+ uploadSnapshot: {
+ invalidatesTags: ['cloud-migration-snapshot'],
},
getDashboardByUid: suppressErrorsOnQuery,
diff --git a/public/app/features/migrate-to-cloud/onprem/Page.tsx b/public/app/features/migrate-to-cloud/onprem/Page.tsx
index c92a1de23c0..91b59f801a4 100644
--- a/public/app/features/migrate-to-cloud/onprem/Page.tsx
+++ b/public/app/features/migrate-to-cloud/onprem/Page.tsx
@@ -1,15 +1,17 @@
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 { Trans, t } from 'app/core/internationalization';
import {
+ SnapshotDto,
+ useCreateSnapshotMutation,
useDeleteSessionMutation,
- useGetCloudMigrationRunListQuery,
- useGetCloudMigrationRunQuery,
useGetSessionListQuery,
- useRunCloudMigrationMutation,
+ useGetShapshotListQuery,
+ useGetSnapshotQuery,
+ useUploadSnapshotMutation,
} from '../api';
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
*/
-function useGetLatestMigrationDestination() {
+function useGetLatestSession() {
const result = useGetSessionListQuery();
const latestMigration = result.data?.sessions?.at(-1);
@@ -42,64 +44,88 @@ function useGetLatestMigrationDestination() {
};
}
-function useGetLatestMigrationRun(migrationUid?: string) {
- const listResult = useGetCloudMigrationRunListQuery(migrationUid ? { uid: migrationUid } : skipToken);
- const latestMigrationRun = listResult.data?.runs?.at(-1);
+const SHOULD_POLL_STATUSES: Array = [
+ 'INITIALIZING',
+ 'CREATING',
+ 'UPLOADING',
+ 'PENDING_PROCESSING',
+ 'PROCESSING',
+];
- const runResult = useGetCloudMigrationRunQuery(
- latestMigrationRun?.uid && migrationUid ? { runUid: latestMigrationRun.uid } : skipToken
- );
+const STATUS_POLL_INTERVAL = 5 * 1000;
+
+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 {
- ...runResult,
+ ...snapshotResult,
- data: runResult.data,
+ error: listResult.error || snapshotResult.error,
- error: listResult.error || runResult.error,
-
- isError: listResult.isError || runResult.isError,
- isLoading: listResult.isLoading || runResult.isLoading,
- isFetching: listResult.isFetching || runResult.isFetching,
+ // isSuccess and isUninitialised should always be from snapshotResult
+ // as only the 'final' values from those are important
+ isError: listResult.isError || snapshotResult.isError,
+ isLoading: listResult.isLoading || snapshotResult.isLoading,
+ isFetching: listResult.isFetching || snapshotResult.isFetching,
};
}
export const Page = () => {
const [disconnectModalOpen, setDisconnectModalOpen] = useState(false);
- const migrationDestination = useGetLatestMigrationDestination();
- const lastMigrationRun = useGetLatestMigrationRun(migrationDestination.data?.uid);
- const [performRunMigration, runMigrationResult] = useRunCloudMigrationMutation();
+ const session = useGetLatestSession();
+ const snapshot = useGetLatestSnapshot(session.data?.uid);
+ const [performCreateSnapshot, createSnapshotResult] = useCreateSnapshotMutation();
+ const [performUploadSnapshot, uploadSnapshotResult] = useUploadSnapshotMutation();
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*
// and all buttons should be disabled
const isBusy =
- runMigrationResult.isLoading ||
- migrationDestination.isFetching ||
- lastMigrationRun.isFetching ||
+ createSnapshotResult.isLoading ||
+ uploadSnapshotResult.isLoading ||
+ session.isLoading ||
+ snapshot.isLoading ||
disconnectResult.isLoading;
- const resources = lastMigrationRun.data?.items;
- const migrationDestUID = migrationDestination.data?.uid;
+ const resources = snapshot.data?.results;
const handleDisconnect = useCallback(async () => {
- if (!migrationDestUID) {
- return;
+ if (sessionUid) {
+ performDisconnect({ uid: sessionUid });
}
+ }, [performDisconnect, sessionUid]);
- const resp = await performDisconnect({ uid: migrationDestUID });
- if (!('error' in resp)) {
- setDisconnectModalOpen(false);
+ const handleCreateSnapshot = useCallback(() => {
+ if (sessionUid) {
+ performCreateSnapshot({ uid: sessionUid });
}
- }, [migrationDestUID, performDisconnect]);
+ }, [performCreateSnapshot, sessionUid]);
- const handleStartMigration = useCallback(() => {
- if (migrationDestination.data?.uid) {
- performRunMigration({ uid: migrationDestination.data?.uid });
+ const handleUploadSnapshot = useCallback(() => {
+ if (sessionUid && snapshotUid) {
+ performUploadSnapshot({ uid: sessionUid, snapshotUid: snapshotUid });
}
- }, [performRunMigration, migrationDestination]);
-
- const migrationMeta = migrationDestination.data;
- const isInitialLoading = migrationDestination.isLoading;
+ }, [performUploadSnapshot, sessionUid, snapshotUid]);
if (isInitialLoading) {
// TODO: better loading state
@@ -111,7 +137,7 @@ export const Page = () => {
return (
<>
- {runMigrationResult.isError && (
+ {createSnapshotResult.isError && (
{
}
/>
+
+
+
+
)}
diff --git a/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/MetricsQueryEditor.tsx b/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/MetricsQueryEditor.tsx
index a312c2ecddd..751392acf44 100644
--- a/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/MetricsQueryEditor.tsx
+++ b/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/MetricsQueryEditor.tsx
@@ -1,4 +1,4 @@
-import { PanelData } from '@grafana/data/src/types';
+import { PanelData } from '@grafana/data';
import { EditorRows, EditorRow, EditorFieldGroup } from '@grafana/experimental';
import { multiResourceCompatibleTypes } from '../../azureMetadata';
diff --git a/public/app/plugins/datasource/cloudwatch/components/QueryEditor/MetricsQueryEditor/SQLBuilderEditor/SQLBuilderSelectRow.tsx b/public/app/plugins/datasource/cloudwatch/components/QueryEditor/MetricsQueryEditor/SQLBuilderEditor/SQLBuilderSelectRow.tsx
index 859031eec9a..d350e18bf66 100644
--- a/public/app/plugins/datasource/cloudwatch/components/QueryEditor/MetricsQueryEditor/SQLBuilderEditor/SQLBuilderSelectRow.tsx
+++ b/public/app/plugins/datasource/cloudwatch/components/QueryEditor/MetricsQueryEditor/SQLBuilderEditor/SQLBuilderSelectRow.tsx
@@ -2,13 +2,15 @@ import { useEffect, useMemo } from 'react';
import { SelectableValue, toOption } from '@grafana/data';
import { EditorField, EditorFieldGroup, EditorSwitch } from '@grafana/experimental';
+import { config } from '@grafana/runtime';
import { Select } from '@grafana/ui';
import { CloudWatchDatasource } from '../../../../datasource';
-import { useDimensionKeys, useMetrics, useNamespaces } from '../../../../hooks';
+import { useAccountOptions, useDimensionKeys, useMetrics, useNamespaces } from '../../../../hooks';
import { STATISTICS } from '../../../../language/cloudwatch-sql/language';
import { CloudWatchMetricsQuery } from '../../../../types';
import { appendTemplateVariables } from '../../../../utils/utils';
+import { Account } from '../../../shared/Account';
import {
getMetricNameFromExpression,
@@ -48,13 +50,24 @@ const SQLBuilderSelectRow = ({ datasource, query, onQueryChange }: SQLBuilderSel
const withSchemaEnabled = isUsingWithSchema(sql.from);
const namespaceOptions = useNamespaces(datasource);
- const metricOptions = useMetrics(datasource, { region: query.region, namespace });
+ const metricOptions = useMetrics(datasource, {
+ region: query.region,
+ namespace,
+ ...(config.featureToggles.cloudWatchCrossAccountQuerying &&
+ config.featureToggles.cloudwatchMetricInsightsCrossAccount
+ ? { accountId: query.accountId }
+ : {}),
+ });
const existingFilters = useMemo(() => stringArrayToDimensions(schemaLabels ?? []), [schemaLabels]);
const unusedDimensionKeys = useDimensionKeys(datasource, {
region: query.region,
namespace,
metricName,
dimensionFilters: existingFilters,
+ ...(config.featureToggles.cloudWatchCrossAccountQuerying &&
+ config.featureToggles.cloudwatchMetricInsightsCrossAccount
+ ? { accountId: query.accountId }
+ : {}),
});
const dimensionKeys = useMemo(
() => (schemaLabels?.length ? [...unusedDimensionKeys, ...schemaLabels.map(toOption)] : unusedDimensionKeys),
@@ -76,9 +89,23 @@ const SQLBuilderSelectRow = ({ datasource, query, onQueryChange }: SQLBuilderSel
return { ...query, sql };
};
+ const accountState = useAccountOptions(datasource.resources, query.region);
return (
<>
+ {config.featureToggles.cloudWatchCrossAccountQuerying &&
+ config.featureToggles.cloudwatchMetricInsightsCrossAccount && (
+ {
+ onQueryChange({
+ ...query,
+ accountId,
+ });
+ }}
+ />
+ )}
);
};
diff --git a/public/app/plugins/panel/table/cells/ImageCellOptionsEditor.tsx b/public/app/plugins/panel/table/cells/ImageCellOptionsEditor.tsx
new file mode 100644
index 00000000000..51e711cd367
--- /dev/null
+++ b/public/app/plugins/panel/table/cells/ImageCellOptionsEditor.tsx
@@ -0,0 +1,33 @@
+import { FormEvent } from 'react';
+
+import { TableImageCellOptions } from '@grafana/schema';
+import { Field, Input } from '@grafana/ui';
+
+import { TableCellEditorProps } from '../TableCellOptionEditor';
+
+export const ImageCellOptionsEditor = ({ cellOptions, onChange }: TableCellEditorProps