mirror of https://github.com/grafana/grafana.git
E2C: Start connecting on-prem to real apis (#85527)
* E2C: Start connecting on-prem to real apis * actually run the migration * show resources * basic dashboards resources * show dashboard title * remove console logs * cleanup 1 * i18n * disconnect * i18n * restore type * use fixed format * fix
This commit is contained in:
parent
0ec48cfdbd
commit
aad79c9400
|
@ -175,7 +175,8 @@ type MigrateDataResponseDTO struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type MigrateDataResponseItemDTO struct {
|
type MigrateDataResponseItemDTO struct {
|
||||||
RefID string `json:"refId"`
|
Type MigrateDataType `json:"type"`
|
||||||
Status ItemStatus `json:"status"`
|
RefID string `json:"refId"`
|
||||||
Error string `json:"error,omitempty"`
|
Status ItemStatus `json:"status"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -15963,9 +15963,15 @@
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"$ref": "#/definitions/ItemStatus"
|
"$ref": "#/definitions/ItemStatus"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/definitions/MigrateDataType"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"MigrateDataType": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"MoveFolderCommand": {
|
"MoveFolderCommand": {
|
||||||
"description": "MoveFolderCommand captures the information required by the folder service\nto move a folder.",
|
"description": "MoveFolderCommand captures the information required by the folder service\nto move a folder.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
|
@ -6,6 +6,9 @@ import { BackendSrvRequest, getBackendSrv } from '@grafana/runtime';
|
||||||
interface RequestOptions extends BackendSrvRequest {
|
interface RequestOptions extends BackendSrvRequest {
|
||||||
manageError?: (err: unknown) => { error: unknown };
|
manageError?: (err: unknown) => { error: unknown };
|
||||||
showErrorAlert?: boolean;
|
showErrorAlert?: boolean;
|
||||||
|
|
||||||
|
// rtk codegen sets this
|
||||||
|
body?: BackendSrvRequest['data'];
|
||||||
}
|
}
|
||||||
|
|
||||||
function createBackendSrvBaseQuery({ baseURL }: { baseURL: string }): BaseQueryFn<RequestOptions> {
|
function createBackendSrvBaseQuery({ baseURL }: { baseURL: string }): BaseQueryFn<RequestOptions> {
|
||||||
|
@ -16,6 +19,7 @@ function createBackendSrvBaseQuery({ baseURL }: { baseURL: string }): BaseQueryF
|
||||||
...requestOptions,
|
...requestOptions,
|
||||||
url: baseURL + requestOptions.url,
|
url: baseURL + requestOptions.url,
|
||||||
showErrorAlert: requestOptions.showErrorAlert,
|
showErrorAlert: requestOptions.showErrorAlert,
|
||||||
|
data: requestOptions.body,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return { data: responseData, meta };
|
return { data: responseData, meta };
|
||||||
|
|
|
@ -25,10 +25,13 @@ const injectedRtkApi = api.injectEndpoints({
|
||||||
createCloudMigrationToken: build.mutation<CreateCloudMigrationTokenApiResponse, CreateCloudMigrationTokenApiArg>({
|
createCloudMigrationToken: build.mutation<CreateCloudMigrationTokenApiResponse, CreateCloudMigrationTokenApiArg>({
|
||||||
query: () => ({ url: `/cloudmigration/token`, method: 'POST' }),
|
query: () => ({ url: `/cloudmigration/token`, method: 'POST' }),
|
||||||
}),
|
}),
|
||||||
|
getDashboardByUid: build.query<GetDashboardByUidApiResponse, GetDashboardByUidApiArg>({
|
||||||
|
query: (queryArg) => ({ url: `/dashboards/uid/${queryArg.uid}` }),
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
overrideExisting: false,
|
overrideExisting: false,
|
||||||
});
|
});
|
||||||
export { injectedRtkApi as enhancedApi };
|
export { injectedRtkApi as generatedAPI };
|
||||||
export type GetMigrationListApiResponse = /** status 200 (empty) */ CloudMigrationListResponse;
|
export type GetMigrationListApiResponse = /** status 200 (empty) */ CloudMigrationListResponse;
|
||||||
export type GetMigrationListApiArg = void;
|
export type GetMigrationListApiArg = void;
|
||||||
export type CreateMigrationApiResponse = /** status 200 (empty) */ CloudMigrationResponse;
|
export type CreateMigrationApiResponse = /** status 200 (empty) */ CloudMigrationResponse;
|
||||||
|
@ -64,6 +67,10 @@ export type GetCloudMigrationRunApiArg = {
|
||||||
};
|
};
|
||||||
export type CreateCloudMigrationTokenApiResponse = /** status 200 (empty) */ CreateAccessTokenResponseDto;
|
export type CreateCloudMigrationTokenApiResponse = /** status 200 (empty) */ CreateAccessTokenResponseDto;
|
||||||
export type CreateCloudMigrationTokenApiArg = void;
|
export type CreateCloudMigrationTokenApiArg = void;
|
||||||
|
export type GetDashboardByUidApiResponse = /** status 200 (empty) */ DashboardFullWithMeta;
|
||||||
|
export type GetDashboardByUidApiArg = {
|
||||||
|
uid: string;
|
||||||
|
};
|
||||||
export type CloudMigrationResponse = {
|
export type CloudMigrationResponse = {
|
||||||
created?: string;
|
created?: string;
|
||||||
id?: number;
|
id?: number;
|
||||||
|
@ -87,10 +94,12 @@ export type CloudMigrationRequest = {
|
||||||
authToken?: string;
|
authToken?: string;
|
||||||
};
|
};
|
||||||
export type ItemStatus = string;
|
export type ItemStatus = string;
|
||||||
|
export type MigrateDataType = string;
|
||||||
export type MigrateDataResponseItemDto = {
|
export type MigrateDataResponseItemDto = {
|
||||||
error?: string;
|
error?: string;
|
||||||
refId?: string;
|
refId?: string;
|
||||||
status?: ItemStatus;
|
status?: ItemStatus;
|
||||||
|
type?: MigrateDataType;
|
||||||
};
|
};
|
||||||
export type MigrateDataResponseDto = {
|
export type MigrateDataResponseDto = {
|
||||||
id?: number;
|
id?: number;
|
||||||
|
@ -102,6 +111,50 @@ export type CloudMigrationRunList = {
|
||||||
export type CreateAccessTokenResponseDto = {
|
export type CreateAccessTokenResponseDto = {
|
||||||
token?: string;
|
token?: string;
|
||||||
};
|
};
|
||||||
|
export type Json = object;
|
||||||
|
export type AnnotationActions = {
|
||||||
|
canAdd?: boolean;
|
||||||
|
canDelete?: boolean;
|
||||||
|
canEdit?: boolean;
|
||||||
|
};
|
||||||
|
export type AnnotationPermission = {
|
||||||
|
dashboard?: AnnotationActions;
|
||||||
|
organization?: AnnotationActions;
|
||||||
|
};
|
||||||
|
export type DashboardMeta = {
|
||||||
|
annotationsPermissions?: AnnotationPermission;
|
||||||
|
canAdmin?: boolean;
|
||||||
|
canDelete?: boolean;
|
||||||
|
canEdit?: boolean;
|
||||||
|
canSave?: boolean;
|
||||||
|
canStar?: boolean;
|
||||||
|
created?: string;
|
||||||
|
createdBy?: string;
|
||||||
|
expires?: string;
|
||||||
|
/** Deprecated: use FolderUID instead */
|
||||||
|
folderId?: number;
|
||||||
|
folderTitle?: string;
|
||||||
|
folderUid?: string;
|
||||||
|
folderUrl?: string;
|
||||||
|
hasAcl?: boolean;
|
||||||
|
isFolder?: boolean;
|
||||||
|
isSnapshot?: boolean;
|
||||||
|
isStarred?: boolean;
|
||||||
|
provisioned?: boolean;
|
||||||
|
provisionedExternalId?: string;
|
||||||
|
publicDashboardEnabled?: boolean;
|
||||||
|
publicDashboardUid?: string;
|
||||||
|
slug?: string;
|
||||||
|
type?: string;
|
||||||
|
updated?: string;
|
||||||
|
updatedBy?: string;
|
||||||
|
url?: string;
|
||||||
|
version?: number;
|
||||||
|
};
|
||||||
|
export type DashboardFullWithMeta = {
|
||||||
|
dashboard?: Json;
|
||||||
|
meta?: DashboardMeta;
|
||||||
|
};
|
||||||
export const {
|
export const {
|
||||||
useGetMigrationListQuery,
|
useGetMigrationListQuery,
|
||||||
useCreateMigrationMutation,
|
useCreateMigrationMutation,
|
||||||
|
@ -111,4 +164,5 @@ export const {
|
||||||
useRunCloudMigrationMutation,
|
useRunCloudMigrationMutation,
|
||||||
useGetCloudMigrationRunQuery,
|
useGetCloudMigrationRunQuery,
|
||||||
useCreateCloudMigrationTokenMutation,
|
useCreateCloudMigrationTokenMutation,
|
||||||
|
useGetDashboardByUidQuery,
|
||||||
} = injectedRtkApi;
|
} = injectedRtkApi;
|
||||||
|
|
|
@ -1,2 +1,39 @@
|
||||||
export * from './endpoints.gen';
|
export * from './endpoints.gen';
|
||||||
export { enhancedApi as cloudMigrationAPI } from './endpoints.gen';
|
import { generatedAPI } from './endpoints.gen';
|
||||||
|
|
||||||
|
export const cloudMigrationAPI = generatedAPI.enhanceEndpoints({
|
||||||
|
addTagTypes: ['cloud-migration-config', 'cloud-migration-run'],
|
||||||
|
endpoints: {
|
||||||
|
// List Cloud Configs
|
||||||
|
getMigrationList: {
|
||||||
|
providesTags: ['cloud-migration-config'] /* should this be a -list? */,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Create Cloud Config
|
||||||
|
createMigration: {
|
||||||
|
invalidatesTags: ['cloud-migration-config'],
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get one Cloud Config
|
||||||
|
getCloudMigration: {
|
||||||
|
providesTags: ['cloud-migration-config'],
|
||||||
|
},
|
||||||
|
|
||||||
|
// Delete one Cloud Config
|
||||||
|
deleteCloudMigration: {
|
||||||
|
invalidatesTags: ['cloud-migration-config'],
|
||||||
|
},
|
||||||
|
|
||||||
|
getCloudMigrationRunList: {
|
||||||
|
providesTags: ['cloud-migration-run'] /* should this be a -list? */,
|
||||||
|
},
|
||||||
|
|
||||||
|
getCloudMigrationRun: {
|
||||||
|
providesTags: ['cloud-migration-run'],
|
||||||
|
},
|
||||||
|
|
||||||
|
runCloudMigration: {
|
||||||
|
invalidatesTags: ['cloud-migration-run'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
@ -41,11 +41,13 @@ export interface ConnectStackDTOMock {
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MigrationResourceDTOMock {
|
type MigrationResourceStatus = 'not-migrated' | 'migrated' | 'migrating' | 'failed';
|
||||||
|
|
||||||
|
export interface MigrationResourceDatasource {
|
||||||
uid: string;
|
uid: string;
|
||||||
status: 'not-migrated' | 'migrated' | 'migrating' | 'failed';
|
status: MigrationResourceStatus;
|
||||||
statusMessage?: string;
|
statusMessage?: string;
|
||||||
type: 'datasource' | 'dashboard'; // TODO: in future this would be a discriminated union with the resource details
|
type: 'datasource';
|
||||||
resource: {
|
resource: {
|
||||||
uid: string;
|
uid: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -54,6 +56,19 @@ export interface MigrationResourceDTOMock {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MigrationResourceDashboard {
|
||||||
|
uid: string;
|
||||||
|
status: MigrationResourceStatus;
|
||||||
|
statusMessage?: string;
|
||||||
|
type: 'dashboard';
|
||||||
|
resource: {
|
||||||
|
uid: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MigrationResourceDTOMock = MigrationResourceDatasource | MigrationResourceDashboard;
|
||||||
|
|
||||||
const mockApplications = ['auth-service', 'web server', 'backend'];
|
const mockApplications = ['auth-service', 'web server', 'backend'];
|
||||||
const mockEnvs = ['DEV', 'PROD'];
|
const mockEnvs = ['DEV', 'PROD'];
|
||||||
const mockRoles = ['db', 'load-balancer', 'server', 'logs'];
|
const mockRoles = ['db', 'load-balancer', 'server', 'logs'];
|
||||||
|
|
|
@ -3,13 +3,12 @@ import React from 'react';
|
||||||
import { Box, Button, ModalsController, Text } from '@grafana/ui';
|
import { Box, Button, ModalsController, Text } from '@grafana/ui';
|
||||||
import { Trans } from 'app/core/internationalization';
|
import { Trans } from 'app/core/internationalization';
|
||||||
|
|
||||||
import { useConnectStackMutationMock, useGetStatusQueryMock } from '../../../mockAPI';
|
import { useCreateMigrationMutation } from '../../../api';
|
||||||
|
|
||||||
import { ConnectModal } from './ConnectModal';
|
import { ConnectModal } from './ConnectModal';
|
||||||
|
|
||||||
export const CallToAction = () => {
|
export const CallToAction = () => {
|
||||||
const [connectStack, connectResponse] = useConnectStackMutationMock();
|
const [createMigration, createMigrationResponse] = useCreateMigrationMutation();
|
||||||
const { isFetching } = useGetStatusQueryMock();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalsController>
|
<ModalsController>
|
||||||
|
@ -19,11 +18,11 @@ export const CallToAction = () => {
|
||||||
<Trans i18nKey="migrate-to-cloud.cta.header">Let us manage your Grafana stack</Trans>
|
<Trans i18nKey="migrate-to-cloud.cta.header">Let us manage your Grafana stack</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
disabled={isFetching || connectResponse.isLoading}
|
disabled={createMigrationResponse.isLoading}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
showModal(ConnectModal, {
|
showModal(ConnectModal, {
|
||||||
hideModal,
|
hideModal,
|
||||||
onConfirm: connectStack,
|
onConfirm: createMigration,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
|
@ -6,16 +6,19 @@ import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { Modal, Button, Stack, TextLink, Field, Input, Text, useStyles2 } from '@grafana/ui';
|
import { Modal, Button, Stack, TextLink, Field, Input, Text, useStyles2 } from '@grafana/ui';
|
||||||
import { Trans, t } from 'app/core/internationalization';
|
import { Trans, t } from 'app/core/internationalization';
|
||||||
|
|
||||||
import { ConnectStackDTOMock } from '../../../mockAPI';
|
import { CreateMigrationApiArg } from '../../../api';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
hideModal: () => void;
|
hideModal: () => void;
|
||||||
onConfirm: (connectStackData: ConnectStackDTOMock) => Promise<{ data: void } | { error: unknown }>;
|
onConfirm: (connectStackData: CreateMigrationApiArg) => Promise<unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormData {
|
||||||
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ConnectModal = ({ hideModal, onConfirm }: Props) => {
|
export const ConnectModal = ({ hideModal, onConfirm }: Props) => {
|
||||||
const [isConnecting, setIsConnecting] = useState(false);
|
const [isConnecting, setIsConnecting] = useState(false);
|
||||||
const cloudStackId = useId();
|
|
||||||
const tokenId = useId();
|
const tokenId = useId();
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
|
@ -24,19 +27,22 @@ export const ConnectModal = ({ hideModal, onConfirm }: Props) => {
|
||||||
register,
|
register,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
watch,
|
watch,
|
||||||
} = useForm<ConnectStackDTOMock>({
|
} = useForm<FormData>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
stackURL: '',
|
|
||||||
token: '',
|
token: '',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const stackURL = watch('stackURL');
|
|
||||||
const token = watch('token');
|
const token = watch('token');
|
||||||
|
|
||||||
const onConfirmConnect: SubmitHandler<ConnectStackDTOMock> = async (formData) => {
|
const onConfirmConnect: SubmitHandler<FormData> = async (formData) => {
|
||||||
setIsConnecting(true);
|
setIsConnecting(true);
|
||||||
await onConfirm(formData);
|
// TODO: location of this is kinda weird, making it tricky to handle errors from this.
|
||||||
|
await onConfirm({
|
||||||
|
cloudMigrationRequest: {
|
||||||
|
authToken: formData.token,
|
||||||
|
},
|
||||||
|
});
|
||||||
setIsConnecting(false);
|
setIsConnecting(false);
|
||||||
hideModal();
|
hideModal();
|
||||||
};
|
};
|
||||||
|
@ -49,46 +55,34 @@ export const ConnectModal = ({ hideModal, onConfirm }: Props) => {
|
||||||
<Trans i18nKey="migrate-to-cloud.connect-modal.body-get-started">
|
<Trans i18nKey="migrate-to-cloud.connect-modal.body-get-started">
|
||||||
To get started, you'll need a Grafana.com account.
|
To get started, you'll need a Grafana.com account.
|
||||||
</Trans>
|
</Trans>
|
||||||
|
|
||||||
<TextLink href="https://grafana.com/auth/sign-up/create-user?pg=prod-cloud" external>
|
<TextLink href="https://grafana.com/auth/sign-up/create-user?pg=prod-cloud" external>
|
||||||
{t('migrate-to-cloud.connect-modal.body-sign-up', 'Sign up for a Grafana.com account')}
|
{t('migrate-to-cloud.connect-modal.body-sign-up', 'Sign up for a Grafana.com account')}
|
||||||
</TextLink>
|
</TextLink>
|
||||||
|
|
||||||
<Trans i18nKey="migrate-to-cloud.connect-modal.body-cloud-stack">
|
<Trans i18nKey="migrate-to-cloud.connect-modal.body-cloud-stack">
|
||||||
You'll also need a cloud stack. If you just signed up, we'll automatically create your first
|
You'll also need a cloud stack. If you just signed up, we'll automatically create your first
|
||||||
stack. If you have an account, you'll need to select or create a stack.
|
stack. If you have an account, you'll need to select or create a stack.
|
||||||
</Trans>
|
</Trans>
|
||||||
|
|
||||||
<TextLink href="https://grafana.com/auth/sign-in/" external>
|
<TextLink href="https://grafana.com/auth/sign-in/" external>
|
||||||
{t('migrate-to-cloud.connect-modal.body-view-stacks', 'View my cloud stacks')}
|
{t('migrate-to-cloud.connect-modal.body-view-stacks', 'View my cloud stacks')}
|
||||||
</TextLink>
|
</TextLink>
|
||||||
<Trans i18nKey="migrate-to-cloud.connect-modal.body-paste-stack">
|
|
||||||
Once you've decided on a stack, paste the URL below.
|
|
||||||
</Trans>
|
|
||||||
<Field
|
|
||||||
className={styles.field}
|
|
||||||
invalid={!!errors.stackURL}
|
|
||||||
error={errors.stackURL?.message}
|
|
||||||
label={t('migrate-to-cloud.connect-modal.body-url-field', 'Cloud stack URL')}
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
{...register('stackURL', {
|
|
||||||
required: t('migrate-to-cloud.connect-modal.stack-required-error', 'Stack URL is required'),
|
|
||||||
})}
|
|
||||||
id={cloudStackId}
|
|
||||||
placeholder="https://example.grafana.net/"
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
<span>
|
<span>
|
||||||
<Trans i18nKey="migrate-to-cloud.connect-modal.body-token">
|
<Trans i18nKey="migrate-to-cloud.connect-modal.body-token">
|
||||||
Your self-managed Grafana installation needs special access to securely migrate content. You'll
|
Your self-managed Grafana installation needs special access to securely migrate content. You'll
|
||||||
need to create a migration token on your chosen cloud stack.
|
need to create a migration token on your chosen cloud stack.
|
||||||
</Trans>
|
</Trans>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
<Trans i18nKey="migrate-to-cloud.connect-modal.body-token-instructions">
|
<Trans i18nKey="migrate-to-cloud.connect-modal.body-token-instructions">
|
||||||
Log into your cloud stack and navigate to Administration, General, Migrate to Grafana Cloud. Create a
|
Log into your cloud stack and navigate to Administration, General, Migrate to Grafana Cloud. Create a
|
||||||
migration token on that screen and paste the token here.
|
migration token on that screen and paste the token here.
|
||||||
</Trans>
|
</Trans>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<Field
|
<Field
|
||||||
className={styles.field}
|
className={styles.field}
|
||||||
invalid={!!errors.token}
|
invalid={!!errors.token}
|
||||||
|
@ -110,7 +104,7 @@ export const ConnectModal = ({ hideModal, onConfirm }: Props) => {
|
||||||
<Button variant="secondary" onClick={hideModal}>
|
<Button variant="secondary" onClick={hideModal}>
|
||||||
<Trans i18nKey="migrate-to-cloud.connect-modal.cancel">Cancel</Trans>
|
<Trans i18nKey="migrate-to-cloud.connect-modal.cancel">Cancel</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" disabled={isConnecting || !(stackURL && token)}>
|
<Button type="submit" disabled={isConnecting || !token}>
|
||||||
{isConnecting
|
{isConnecting
|
||||||
? t('migrate-to-cloud.connect-modal.connecting', 'Connecting to this stack...')
|
? t('migrate-to-cloud.connect-modal.connecting', 'Connecting to this stack...')
|
||||||
: t('migrate-to-cloud.connect-modal.connect', 'Connect to this stack')}
|
: t('migrate-to-cloud.connect-modal.connect', 'Connect to this stack')}
|
||||||
|
|
|
@ -15,6 +15,7 @@ export const EmptyState = () => {
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<Stack direction="column">
|
<Stack direction="column">
|
||||||
<CallToAction />
|
<CallToAction />
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
alignItems="flex-start"
|
alignItems="flex-start"
|
||||||
gap={1}
|
gap={1}
|
||||||
|
|
|
@ -1,43 +1,139 @@
|
||||||
import { skipToken } from '@reduxjs/toolkit/query/react';
|
import { skipToken } from '@reduxjs/toolkit/query/react';
|
||||||
import React, { useState } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
|
import { config } from '@grafana/runtime';
|
||||||
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 { useGetStatusQueryMock, useListResourcesQueryMock, useStartMigrationMutationMock } from '../mockAPI';
|
import {
|
||||||
|
MigrateDataResponseDto,
|
||||||
|
useDeleteCloudMigrationMutation,
|
||||||
|
useGetCloudMigrationRunListQuery,
|
||||||
|
useGetCloudMigrationRunQuery,
|
||||||
|
useGetMigrationListQuery,
|
||||||
|
useRunCloudMigrationMutation,
|
||||||
|
} from '../api';
|
||||||
|
import { MigrationResourceDTOMock } from '../mockAPI';
|
||||||
|
|
||||||
import { DisconnectModal } from './DisconnectModal';
|
|
||||||
import { EmptyState } from './EmptyState/EmptyState';
|
import { EmptyState } from './EmptyState/EmptyState';
|
||||||
import { MigrationInfo } from './MigrationInfo';
|
import { MigrationInfo } from './MigrationInfo';
|
||||||
import { ResourcesTable } from './ResourcesTable';
|
import { ResourcesTable } from './ResourcesTable';
|
||||||
|
|
||||||
export const Page = () => {
|
/**
|
||||||
const { data: status, isFetching } = useGetStatusQueryMock();
|
* Here's how migrations work:
|
||||||
const { data: resources } = useListResourcesQueryMock(status?.enabled ? undefined : skipToken);
|
*
|
||||||
const [startMigration, { isLoading: startMigrationIsLoading, isError: startMigrationIsError }] =
|
* A single on-prem instance can be configured to be migrated to multiple cloud instances.
|
||||||
useStartMigrationMutationMock();
|
* - GetMigrationList returns this the list of migration targets for the on prem instance
|
||||||
const [isDisconnecting, setIsDisconnecting] = useState(false);
|
* - If GetMigrationList returns an empty list, then the empty state with a prompt to enter a token should be shown
|
||||||
|
* - The UI (at the moment) only shows the most recently created migration target (the last one returned from the API)
|
||||||
|
* and doesn't allow for others to be created
|
||||||
|
*
|
||||||
|
* A single on-prem migration 'target' (CloudMigrationResponse) can have multiple migration runs (CloudMigrationRun)
|
||||||
|
* - To list the migration resources:
|
||||||
|
* 1. call GetCloudMigratiopnRunList to list all runs
|
||||||
|
* 2. call GetCloudMigrationRun with the ID from first step to list the result of that migration
|
||||||
|
*/
|
||||||
|
|
||||||
if (!status?.enabled) {
|
function useGetLatestMigrationDestination() {
|
||||||
|
const result = useGetMigrationListQuery();
|
||||||
|
const latestMigration = result.data?.migrations?.[0];
|
||||||
|
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
data: latestMigration,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function useGetLatestMigrationRun(migrationId?: number) {
|
||||||
|
const listResult = useGetCloudMigrationRunListQuery(migrationId ? { id: migrationId } : skipToken);
|
||||||
|
const latestMigrationRun = listResult.data?.runs?.[0];
|
||||||
|
|
||||||
|
const runResult = useGetCloudMigrationRunQuery(
|
||||||
|
latestMigrationRun?.id && migrationId ? { runId: latestMigrationRun.id, id: migrationId } : skipToken
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...runResult,
|
||||||
|
|
||||||
|
data: runResult.data,
|
||||||
|
|
||||||
|
error: listResult.error || runResult.error,
|
||||||
|
|
||||||
|
isError: listResult.isError || runResult.isError,
|
||||||
|
isLoading: listResult.isLoading || runResult.isLoading,
|
||||||
|
isFetching: listResult.isFetching || runResult.isFetching,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Page = () => {
|
||||||
|
const migrationDestination = useGetLatestMigrationDestination();
|
||||||
|
const lastMigrationRun = useGetLatestMigrationRun(migrationDestination.data?.id);
|
||||||
|
const [performRunMigration, runMigrationResult] = useRunCloudMigrationMutation();
|
||||||
|
const [performDisconnect, disconnectResult] = useDeleteCloudMigrationMutation();
|
||||||
|
|
||||||
|
// 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 ||
|
||||||
|
disconnectResult.isLoading;
|
||||||
|
|
||||||
|
const resources = useFixResources(lastMigrationRun.data);
|
||||||
|
|
||||||
|
const handleDisconnect = useCallback(() => {
|
||||||
|
if (migrationDestination.data?.id) {
|
||||||
|
performDisconnect({
|
||||||
|
id: migrationDestination.data.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [migrationDestination.data?.id, performDisconnect]);
|
||||||
|
|
||||||
|
const handleStartMigration = useCallback(() => {
|
||||||
|
if (migrationDestination.data?.id) {
|
||||||
|
performRunMigration({ id: migrationDestination.data?.id });
|
||||||
|
}
|
||||||
|
}, [performRunMigration, migrationDestination]);
|
||||||
|
|
||||||
|
const migrationMeta = migrationDestination.data;
|
||||||
|
const isInitialLoading = migrationDestination.isLoading;
|
||||||
|
|
||||||
|
if (isInitialLoading) {
|
||||||
|
// TODO: better loading state
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
} else if (!migrationMeta) {
|
||||||
return <EmptyState />;
|
return <EmptyState />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isBusy = isFetching || isDisconnecting || startMigrationIsLoading;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack direction="column" gap={4}>
|
<Stack direction="column" gap={4}>
|
||||||
{startMigrationIsError && (
|
{runMigrationResult.isError && (
|
||||||
<Alert
|
<Alert
|
||||||
severity="error"
|
severity="error"
|
||||||
title={t(
|
title={t(
|
||||||
'migrate-to-cloud.summary.error-starting-migration',
|
'migrate-to-cloud.summary.run-migration-error-title',
|
||||||
'There was an error starting cloud migration'
|
'There was an error migrating your resources'
|
||||||
)}
|
)}
|
||||||
/>
|
>
|
||||||
|
<Trans i18nKey="migrate-to-cloud.summary.run-migration-error-description">
|
||||||
|
See the Grafana server logs for more details
|
||||||
|
</Trans>
|
||||||
|
</Alert>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{status.stackURL && (
|
{disconnectResult.isError && (
|
||||||
|
<Alert
|
||||||
|
severity="error"
|
||||||
|
title={t('migrate-to-cloud.summary.disconnect-error-title', 'There was an error disconnecting')}
|
||||||
|
>
|
||||||
|
<Trans i18nKey="migrate-to-cloud.summary.disconnect-error-description">
|
||||||
|
See the Grafana server logs for more details
|
||||||
|
</Trans>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{migrationMeta.stack && (
|
||||||
<Box
|
<Box
|
||||||
borderColor="weak"
|
borderColor="weak"
|
||||||
borderStyle="solid"
|
borderStyle="solid"
|
||||||
|
@ -51,8 +147,14 @@ export const Page = () => {
|
||||||
title={t('migrate-to-cloud.summary.target-stack-title', 'Uploading to')}
|
title={t('migrate-to-cloud.summary.target-stack-title', 'Uploading to')}
|
||||||
value={
|
value={
|
||||||
<>
|
<>
|
||||||
{status.stackURL}{' '}
|
{migrationMeta.stack}{' '}
|
||||||
<Button onClick={() => setIsDisconnecting(true)} disabled={isBusy} variant="secondary" size="sm">
|
<Button
|
||||||
|
disabled={isBusy}
|
||||||
|
onClick={handleDisconnect}
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
icon={disconnectResult.isLoading ? 'spinner' : undefined}
|
||||||
|
>
|
||||||
<Trans i18nKey="migrate-to-cloud.summary.disconnect">Disconnect</Trans>
|
<Trans i18nKey="migrate-to-cloud.summary.disconnect">Disconnect</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
|
@ -61,8 +163,8 @@ export const Page = () => {
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
disabled={isBusy}
|
disabled={isBusy}
|
||||||
onClick={() => startMigration()}
|
onClick={handleStartMigration}
|
||||||
icon={startMigrationIsLoading ? 'spinner' : undefined}
|
icon={runMigrationResult.isLoading ? 'spinner' : undefined}
|
||||||
>
|
>
|
||||||
<Trans i18nKey="migrate-to-cloud.summary.start-migration">Upload everything</Trans>
|
<Trans i18nKey="migrate-to-cloud.summary.start-migration">Upload everything</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -72,7 +174,65 @@ export const Page = () => {
|
||||||
{resources && <ResourcesTable resources={resources} />}
|
{resources && <ResourcesTable resources={resources} />}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<DisconnectModal isOpen={isDisconnecting} onDismiss={() => setIsDisconnecting(false)} />
|
{/* <DisconnectModal isOpen={isDisconnecting} onDismiss={() => setIsDisconnecting(false)} /> */}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// converts API status to our expected/mocked status
|
||||||
|
function convertStatus(status: string) {
|
||||||
|
switch (status) {
|
||||||
|
case 'OK':
|
||||||
|
return 'migrated';
|
||||||
|
case 'ERROR':
|
||||||
|
return 'failed';
|
||||||
|
case 'failed':
|
||||||
|
return 'failed';
|
||||||
|
default:
|
||||||
|
return 'failed';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function useFixResources(data: MigrateDataResponseDto | undefined) {
|
||||||
|
return useMemo(() => {
|
||||||
|
if (!data?.items) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const betterResources: MigrationResourceDTOMock[] = data.items.flatMap((item) => {
|
||||||
|
if (item.type === 'DATASOURCE') {
|
||||||
|
const datasourceConfig = Object.values(config.datasources).find((v) => v.uid === item.refId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'datasource',
|
||||||
|
uid: item.refId ?? '',
|
||||||
|
status: convertStatus(item.status ?? ''),
|
||||||
|
statusMessage: item.error,
|
||||||
|
resource: {
|
||||||
|
uid: item.refId ?? '',
|
||||||
|
name: datasourceConfig?.name ?? 'Unknown data source',
|
||||||
|
type: datasourceConfig?.meta?.name ?? 'Unknown type',
|
||||||
|
icon: datasourceConfig?.meta?.info?.logos?.small,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.type === 'DASHBOARD') {
|
||||||
|
return {
|
||||||
|
type: 'dashboard',
|
||||||
|
uid: item.refId ?? '',
|
||||||
|
status: convertStatus(item.status ?? ''),
|
||||||
|
statusMessage: item.error,
|
||||||
|
resource: {
|
||||||
|
uid: item.refId ?? '',
|
||||||
|
name: item.refId ?? 'Unknown dashboard',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
|
return betterResources;
|
||||||
|
}, [data]);
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
|
import Skeleton from 'react-loading-skeleton';
|
||||||
|
|
||||||
import { InteractiveTable, CellProps, Stack, Text, Icon, useStyles2, Button } from '@grafana/ui';
|
import { InteractiveTable, CellProps, Stack, Text, Icon, useStyles2, Button } from '@grafana/ui';
|
||||||
import { getSvgSize } from '@grafana/ui/src/components/Icon/utils';
|
import { getSvgSize } from '@grafana/ui/src/components/Icon/utils';
|
||||||
import { t } from 'app/core/internationalization';
|
import { t } from 'app/core/internationalization';
|
||||||
|
|
||||||
import { MigrationResourceDTOMock } from '../mockAPI';
|
import { useGetDashboardByUidQuery } from '../api';
|
||||||
|
import { MigrationResourceDTOMock, MigrationResourceDashboard, MigrationResourceDatasource } from '../mockAPI';
|
||||||
|
|
||||||
interface ResourcesTableProps {
|
interface ResourcesTableProps {
|
||||||
resources: MigrationResourceDTOMock[];
|
resources: MigrationResourceDTOMock[];
|
||||||
|
@ -23,18 +25,66 @@ export function ResourcesTable({ resources }: ResourcesTableProps) {
|
||||||
|
|
||||||
function NameCell(props: CellProps<MigrationResourceDTOMock>) {
|
function NameCell(props: CellProps<MigrationResourceDTOMock>) {
|
||||||
const data = props.row.original;
|
const data = props.row.original;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack direction="row" gap={2} alignItems="center">
|
<Stack direction="row" gap={2} alignItems="center">
|
||||||
<ResourceIcon resource={data} />
|
<ResourceIcon resource={data} />
|
||||||
|
|
||||||
<Stack direction="column" gap={0}>
|
<Stack direction="column" gap={0}>
|
||||||
<span>{data.resource.name}</span>
|
{data.type === 'datasource' ? <DatasourceInfo data={data} /> : <DashboardInfo data={data} />}
|
||||||
<Text color="secondary">{data.resource.type}</Text>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDashboardTitle(dashboardData: object) {
|
||||||
|
if ('title' in dashboardData && typeof dashboardData.title === 'string') {
|
||||||
|
return dashboardData.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DatasourceInfo({ data }: { data: MigrationResourceDatasource }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span>{data.resource.name}</span>
|
||||||
|
<Text color="secondary">{data.resource.type}</Text>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: really, the API should return this directly
|
||||||
|
function DashboardInfo({ data }: { data: MigrationResourceDashboard }) {
|
||||||
|
const { data: dashboardData } = useGetDashboardByUidQuery({
|
||||||
|
uid: data.resource.uid,
|
||||||
|
});
|
||||||
|
|
||||||
|
const dashboardName = useMemo(() => {
|
||||||
|
return (dashboardData?.dashboard && getDashboardTitle(dashboardData.dashboard)) ?? data.resource.uid;
|
||||||
|
}, [dashboardData, data.resource.uid]);
|
||||||
|
|
||||||
|
if (!dashboardData) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span>
|
||||||
|
<Skeleton width={250} />
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<Skeleton width={130} />
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span>{dashboardName}</span>
|
||||||
|
<Text color="secondary">{dashboardData.meta?.folderTitle ?? 'Dashboards'}</Text>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function TypeCell(props: CellProps<MigrationResourceDTOMock>) {
|
function TypeCell(props: CellProps<MigrationResourceDTOMock>) {
|
||||||
const { type } = props.row.original;
|
const { type } = props.row.original;
|
||||||
|
|
||||||
|
|
|
@ -703,18 +703,15 @@
|
||||||
"connect-modal": {
|
"connect-modal": {
|
||||||
"body-cloud-stack": "You'll also need a cloud stack. If you just signed up, we'll automatically create your first stack. If you have an account, you'll need to select or create a stack.",
|
"body-cloud-stack": "You'll also need a cloud stack. If you just signed up, we'll automatically create your first stack. If you have an account, you'll need to select or create a stack.",
|
||||||
"body-get-started": "To get started, you'll need a Grafana.com account.",
|
"body-get-started": "To get started, you'll need a Grafana.com account.",
|
||||||
"body-paste-stack": "Once you've decided on a stack, paste the URL below.",
|
|
||||||
"body-sign-up": "Sign up for a Grafana.com account",
|
"body-sign-up": "Sign up for a Grafana.com account",
|
||||||
"body-token": "Your self-managed Grafana installation needs special access to securely migrate content. You'll need to create a migration token on your chosen cloud stack.",
|
"body-token": "Your self-managed Grafana installation needs special access to securely migrate content. You'll need to create a migration token on your chosen cloud stack.",
|
||||||
"body-token-field": "Migration token",
|
"body-token-field": "Migration token",
|
||||||
"body-token-field-placeholder": "Paste token here",
|
"body-token-field-placeholder": "Paste token here",
|
||||||
"body-token-instructions": "Log into your cloud stack and navigate to Administration, General, Migrate to Grafana Cloud. Create a migration token on that screen and paste the token here.",
|
"body-token-instructions": "Log into your cloud stack and navigate to Administration, General, Migrate to Grafana Cloud. Create a migration token on that screen and paste the token here.",
|
||||||
"body-url-field": "Cloud stack URL",
|
|
||||||
"body-view-stacks": "View my cloud stacks",
|
"body-view-stacks": "View my cloud stacks",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"connect": "Connect to this stack",
|
"connect": "Connect to this stack",
|
||||||
"connecting": "Connecting to this stack...",
|
"connecting": "Connecting to this stack...",
|
||||||
"stack-required-error": "Stack URL is required",
|
|
||||||
"title": "Connect to a cloud stack",
|
"title": "Connect to a cloud stack",
|
||||||
"token-required-error": "Migration token is required"
|
"token-required-error": "Migration token is required"
|
||||||
},
|
},
|
||||||
|
@ -796,7 +793,10 @@
|
||||||
},
|
},
|
||||||
"summary": {
|
"summary": {
|
||||||
"disconnect": "Disconnect",
|
"disconnect": "Disconnect",
|
||||||
"error-starting-migration": "There was an error starting cloud migration",
|
"disconnect-error-description": "See the Grafana server logs for more details",
|
||||||
|
"disconnect-error-title": "There was an error disconnecting",
|
||||||
|
"run-migration-error-description": "See the Grafana server logs for more details",
|
||||||
|
"run-migration-error-title": "There was an error migrating your resources",
|
||||||
"start-migration": "Upload everything",
|
"start-migration": "Upload everything",
|
||||||
"target-stack-title": "Uploading to"
|
"target-stack-title": "Uploading to"
|
||||||
},
|
},
|
||||||
|
|
|
@ -703,18 +703,15 @@
|
||||||
"connect-modal": {
|
"connect-modal": {
|
||||||
"body-cloud-stack": "Ÿőū'ľľ äľşő ʼnęęđ ä čľőūđ şŧäčĸ. Ĩƒ yőū ĵūşŧ şįģʼnęđ ūp, ŵę'ľľ äūŧőmäŧįčäľľy čřęäŧę yőūř ƒįřşŧ şŧäčĸ. Ĩƒ yőū ĥävę äʼn äččőūʼnŧ, yőū'ľľ ʼnęęđ ŧő şęľęčŧ őř čřęäŧę ä şŧäčĸ.",
|
"body-cloud-stack": "Ÿőū'ľľ äľşő ʼnęęđ ä čľőūđ şŧäčĸ. Ĩƒ yőū ĵūşŧ şįģʼnęđ ūp, ŵę'ľľ äūŧőmäŧįčäľľy čřęäŧę yőūř ƒįřşŧ şŧäčĸ. Ĩƒ yőū ĥävę äʼn äččőūʼnŧ, yőū'ľľ ʼnęęđ ŧő şęľęčŧ őř čřęäŧę ä şŧäčĸ.",
|
||||||
"body-get-started": "Ŧő ģęŧ şŧäřŧęđ, yőū'ľľ ʼnęęđ ä Ğřäƒäʼnä.čőm äččőūʼnŧ.",
|
"body-get-started": "Ŧő ģęŧ şŧäřŧęđ, yőū'ľľ ʼnęęđ ä Ğřäƒäʼnä.čőm äččőūʼnŧ.",
|
||||||
"body-paste-stack": "Øʼnčę yőū'vę đęčįđęđ őʼn ä şŧäčĸ, päşŧę ŧĥę ŮŖĿ þęľőŵ.",
|
|
||||||
"body-sign-up": "Ŝįģʼn ūp ƒőř ä Ğřäƒäʼnä.čőm äččőūʼnŧ",
|
"body-sign-up": "Ŝįģʼn ūp ƒőř ä Ğřäƒäʼnä.čőm äččőūʼnŧ",
|
||||||
"body-token": "Ÿőūř şęľƒ-mäʼnäģęđ Ğřäƒäʼnä įʼnşŧäľľäŧįőʼn ʼnęęđş şpęčįäľ äččęşş ŧő şęčūřęľy mįģřäŧę čőʼnŧęʼnŧ. Ÿőū'ľľ ʼnęęđ ŧő čřęäŧę ä mįģřäŧįőʼn ŧőĸęʼn őʼn yőūř čĥőşęʼn čľőūđ şŧäčĸ.",
|
"body-token": "Ÿőūř şęľƒ-mäʼnäģęđ Ğřäƒäʼnä įʼnşŧäľľäŧįőʼn ʼnęęđş şpęčįäľ äččęşş ŧő şęčūřęľy mįģřäŧę čőʼnŧęʼnŧ. Ÿőū'ľľ ʼnęęđ ŧő čřęäŧę ä mįģřäŧįőʼn ŧőĸęʼn őʼn yőūř čĥőşęʼn čľőūđ şŧäčĸ.",
|
||||||
"body-token-field": "Mįģřäŧįőʼn ŧőĸęʼn",
|
"body-token-field": "Mįģřäŧįőʼn ŧőĸęʼn",
|
||||||
"body-token-field-placeholder": "Päşŧę ŧőĸęʼn ĥęřę",
|
"body-token-field-placeholder": "Päşŧę ŧőĸęʼn ĥęřę",
|
||||||
"body-token-instructions": "Ŀőģ įʼnŧő yőūř čľőūđ şŧäčĸ äʼnđ ʼnävįģäŧę ŧő Åđmįʼnįşŧřäŧįőʼn, Ğęʼnęřäľ, Mįģřäŧę ŧő Ğřäƒäʼnä Cľőūđ. Cřęäŧę ä mįģřäŧįőʼn ŧőĸęʼn őʼn ŧĥäŧ şčřęęʼn äʼnđ päşŧę ŧĥę ŧőĸęʼn ĥęřę.",
|
"body-token-instructions": "Ŀőģ įʼnŧő yőūř čľőūđ şŧäčĸ äʼnđ ʼnävįģäŧę ŧő Åđmįʼnįşŧřäŧįőʼn, Ğęʼnęřäľ, Mįģřäŧę ŧő Ğřäƒäʼnä Cľőūđ. Cřęäŧę ä mįģřäŧįőʼn ŧőĸęʼn őʼn ŧĥäŧ şčřęęʼn äʼnđ päşŧę ŧĥę ŧőĸęʼn ĥęřę.",
|
||||||
"body-url-field": "Cľőūđ şŧäčĸ ŮŖĿ",
|
|
||||||
"body-view-stacks": "Vįęŵ my čľőūđ şŧäčĸş",
|
"body-view-stacks": "Vįęŵ my čľőūđ şŧäčĸş",
|
||||||
"cancel": "Cäʼnčęľ",
|
"cancel": "Cäʼnčęľ",
|
||||||
"connect": "Cőʼnʼnęčŧ ŧő ŧĥįş şŧäčĸ",
|
"connect": "Cőʼnʼnęčŧ ŧő ŧĥįş şŧäčĸ",
|
||||||
"connecting": "Cőʼnʼnęčŧįʼnģ ŧő ŧĥįş şŧäčĸ...",
|
"connecting": "Cőʼnʼnęčŧįʼnģ ŧő ŧĥįş şŧäčĸ...",
|
||||||
"stack-required-error": "Ŝŧäčĸ ŮŖĿ įş řęqūįřęđ",
|
|
||||||
"title": "Cőʼnʼnęčŧ ŧő ä čľőūđ şŧäčĸ",
|
"title": "Cőʼnʼnęčŧ ŧő ä čľőūđ şŧäčĸ",
|
||||||
"token-required-error": "Mįģřäŧįőʼn ŧőĸęʼn įş řęqūįřęđ"
|
"token-required-error": "Mįģřäŧįőʼn ŧőĸęʼn įş řęqūįřęđ"
|
||||||
},
|
},
|
||||||
|
@ -796,7 +793,10 @@
|
||||||
},
|
},
|
||||||
"summary": {
|
"summary": {
|
||||||
"disconnect": "Đįşčőʼnʼnęčŧ",
|
"disconnect": "Đįşčőʼnʼnęčŧ",
|
||||||
"error-starting-migration": "Ŧĥęřę ŵäş äʼn ęřřőř şŧäřŧįʼnģ čľőūđ mįģřäŧįőʼn",
|
"disconnect-error-description": "Ŝęę ŧĥę Ğřäƒäʼnä şęřvęř ľőģş ƒőř mőřę đęŧäįľş",
|
||||||
|
"disconnect-error-title": "Ŧĥęřę ŵäş äʼn ęřřőř đįşčőʼnʼnęčŧįʼnģ",
|
||||||
|
"run-migration-error-description": "Ŝęę ŧĥę Ğřäƒäʼnä şęřvęř ľőģş ƒőř mőřę đęŧäįľş",
|
||||||
|
"run-migration-error-title": "Ŧĥęřę ŵäş äʼn ęřřőř mįģřäŧįʼnģ yőūř řęşőūřčęş",
|
||||||
"start-migration": "Ůpľőäđ ęvęřyŧĥįʼnģ",
|
"start-migration": "Ůpľőäđ ęvęřyŧĥįʼnģ",
|
||||||
"target-stack-title": "Ůpľőäđįʼnģ ŧő"
|
"target-stack-title": "Ůpľőäđįʼnģ ŧő"
|
||||||
},
|
},
|
||||||
|
|
|
@ -6740,10 +6740,16 @@
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"$ref": "#/components/schemas/ItemStatus"
|
"$ref": "#/components/schemas/ItemStatus"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"$ref": "#/components/schemas/MigrateDataType"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"MigrateDataType": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"MoveFolderCommand": {
|
"MoveFolderCommand": {
|
||||||
"description": "MoveFolderCommand captures the information required by the folder service\nto move a folder.",
|
"description": "MoveFolderCommand captures the information required by the folder service\nto move a folder.",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
@ -5,6 +5,7 @@ const config: ConfigFile = {
|
||||||
schemaFile: '../public/openapi3.json',
|
schemaFile: '../public/openapi3.json',
|
||||||
apiFile: '', // leave this empty, and instead populate the outputFiles object below
|
apiFile: '', // leave this empty, and instead populate the outputFiles object below
|
||||||
hooks: true,
|
hooks: true,
|
||||||
|
exportName: 'generatedAPI',
|
||||||
|
|
||||||
outputFiles: {
|
outputFiles: {
|
||||||
'../public/app/features/migrate-to-cloud/api/endpoints.gen.ts': {
|
'../public/app/features/migrate-to-cloud/api/endpoints.gen.ts': {
|
||||||
|
@ -19,6 +20,7 @@ const config: ConfigFile = {
|
||||||
'getCloudMigrationRun',
|
'getCloudMigrationRun',
|
||||||
'getCloudMigrationRunList',
|
'getCloudMigrationRunList',
|
||||||
'deleteCloudMigration',
|
'deleteCloudMigration',
|
||||||
|
'getDashboardByUid',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue