mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-12 11:08:31 -05:00
First check in for Migration Dashboard (#14309)
* Adding Dashboard Fixing auth keys api create status Dialog * making some changes requested in the PR * switched to text component from dom component * Adding TODO comment * Fixing image url to work on windows * Fixing stuff pointed out in PR comments * adding return type to dasboard register function * Adding more todos
This commit is contained in:
@@ -127,8 +127,8 @@ export async function getMigrationControllerAuthKeys(account: azdata.Account, su
|
||||
throw new Error(response.errors.toString());
|
||||
}
|
||||
return {
|
||||
keyName1: response?.response?.data?.keyName1 ?? '',
|
||||
keyName2: response?.response?.data?.keyName2 ?? ''
|
||||
authKey1: response?.response?.data?.authKey1 ?? '',
|
||||
authKey2: response?.response?.data?.authKey2 ?? ''
|
||||
};
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ export async function getMigrationControllerMonitoringData(account: azdata.Accou
|
||||
return response.response.data;
|
||||
}
|
||||
|
||||
export async function startDatabaseMigration(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string, managedInstance: string, migrationControllerName: string, requestBody: StartDatabaseMigrationRequest): Promise<any> {
|
||||
export async function startDatabaseMigration(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string, managedInstance: string, migrationControllerName: string, requestBody: StartDatabaseMigrationRequest): Promise<StartDatabaseMigrationResponse> {
|
||||
const api = await getAzureCoreAPI();
|
||||
const host = `https://${regionName}.management.azure.com`;
|
||||
const path = `/subscriptions/${subscription.id}/resourceGroups/${resourceGroupName}/providers/Microsoft.Sql/managedInstances/${managedInstance}/providers/Microsoft.DataMigration/databaseMigrations/${migrationControllerName}?api-version=2020-09-01-preview`;
|
||||
@@ -166,12 +166,23 @@ export async function startDatabaseMigration(account: azdata.Account, subscripti
|
||||
throw new Error(response.errors.toString());
|
||||
}
|
||||
return {
|
||||
errors: response.errors,
|
||||
status: response.response.status,
|
||||
databaseMigration: response.response.data
|
||||
};
|
||||
}
|
||||
|
||||
export async function getMigrationStatus(account: azdata.Account, subscription: Subscription, migration: DatabaseMigration): Promise<any> {
|
||||
const api = await getAzureCoreAPI();
|
||||
const host = `https://eastus2euap.management.azure.com`;
|
||||
const path = `${migration.id}?$expand=MigrationStatusDetails&api-version=2020-09-01-preview`;
|
||||
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host);
|
||||
if (response.errors.length > 0) {
|
||||
throw new Error(response.errors.toString());
|
||||
}
|
||||
return {
|
||||
result: response.response.data
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* For now only east us euap is supported. Actual API calls will be added in the public release.
|
||||
@@ -223,8 +234,8 @@ export interface MigrationController {
|
||||
}
|
||||
|
||||
export interface GetMigrationControllerAuthKeysResult {
|
||||
keyName1: string,
|
||||
keyName2: string
|
||||
authKey1: string,
|
||||
authKey2: string
|
||||
}
|
||||
|
||||
export interface GetStorageAccountAccessKeysResult {
|
||||
@@ -285,3 +296,8 @@ export interface DatabaseMigration {
|
||||
name: string,
|
||||
type: string
|
||||
}
|
||||
|
||||
export interface StartDatabaseMigrationResponse {
|
||||
status: number,
|
||||
databaseMigration: DatabaseMigration
|
||||
}
|
||||
|
||||
@@ -11,21 +11,37 @@ export interface IconPath {
|
||||
}
|
||||
|
||||
export class IconPathHelper {
|
||||
private static context: vscode.ExtensionContext;
|
||||
|
||||
public static copy: IconPath;
|
||||
public static refresh: IconPath;
|
||||
public static sqlMiImportHelpThumbnail: IconPath;
|
||||
public static sqlVmImportHelpThumbnail: IconPath;
|
||||
public static migrationDashboardHeaderBackground: IconPath;
|
||||
public static sqlMigrationLogo: IconPath;
|
||||
|
||||
public static setExtensionContext(context: vscode.ExtensionContext) {
|
||||
IconPathHelper.context = context;
|
||||
IconPathHelper.copy = {
|
||||
light: IconPathHelper.context.asAbsolutePath('images/copy.svg'),
|
||||
dark: IconPathHelper.context.asAbsolutePath('images/copy.svg')
|
||||
light: context.asAbsolutePath('images/copy.svg'),
|
||||
dark: context.asAbsolutePath('images/copy.svg')
|
||||
};
|
||||
IconPathHelper.refresh = {
|
||||
light: context.asAbsolutePath('images/refresh.svg'),
|
||||
dark: context.asAbsolutePath('images/refresh.svg')
|
||||
};
|
||||
|
||||
IconPathHelper.sqlMiImportHelpThumbnail = {
|
||||
light: context.asAbsolutePath('images/sqlMiImportHelpThumbnail.svg'),
|
||||
dark: context.asAbsolutePath('images/sqlMiImportHelpThumbnail.svg')
|
||||
};
|
||||
IconPathHelper.sqlVmImportHelpThumbnail = {
|
||||
light: context.asAbsolutePath('images/sqlVmImportHelpThumbnail.svg'),
|
||||
dark: context.asAbsolutePath('images/sqlVmImportHelpThumbnail.svg')
|
||||
};
|
||||
IconPathHelper.migrationDashboardHeaderBackground = {
|
||||
light: context.asAbsolutePath('images/background.svg'),
|
||||
dark: context.asAbsolutePath('images/background.svg')
|
||||
};
|
||||
IconPathHelper.sqlMigrationLogo = {
|
||||
light: context.asAbsolutePath('images/migration.svg'),
|
||||
dark: context.asAbsolutePath('images/migration.svg')
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
499
extensions/sql-migration/src/dashboard/sqlServerDashboard.ts
Normal file
499
extensions/sql-migration/src/dashboard/sqlServerDashboard.ts
Normal file
@@ -0,0 +1,499 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import { MigrationLocalStorage } from '../models/migrationLocalStorage';
|
||||
import * as loc from '../models/strings';
|
||||
import { IconPathHelper } from '../constants/iconPathHelper';
|
||||
|
||||
interface IActionMetadata {
|
||||
title?: string,
|
||||
description?: string,
|
||||
link?: string,
|
||||
iconPath?: { light: string | vscode.Uri; dark: string | vscode.Uri },
|
||||
command?: string;
|
||||
}
|
||||
|
||||
const maxWidth = 800;
|
||||
|
||||
export class DashboardWidget {
|
||||
|
||||
private _migrationStatusCardsContainer!: azdata.FlexContainer;
|
||||
private _view!: azdata.ModelView;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public register(): void {
|
||||
azdata.ui.registerModelViewProvider('migration.dashboard', async (view) => {
|
||||
this._view = view;
|
||||
const container = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column',
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}).component();
|
||||
const header = this.createHeader(view);
|
||||
|
||||
const tasksContainer = await this.createTasks(view);
|
||||
|
||||
container.addItem(header, {
|
||||
CSSStyles: {
|
||||
'background-image': `url(${vscode.Uri.file(<string>IconPathHelper.migrationDashboardHeaderBackground.light)})`,
|
||||
'width': '1100px',
|
||||
'height': '300px',
|
||||
'background-size': '100%',
|
||||
}
|
||||
});
|
||||
header.addItem(tasksContainer, {
|
||||
CSSStyles: {
|
||||
'width': `${maxWidth}px`,
|
||||
'height': '150px',
|
||||
}
|
||||
});
|
||||
|
||||
header.addItem(await this.createFooter(view), {
|
||||
CSSStyles: {
|
||||
'margin-top': '20px'
|
||||
}
|
||||
});
|
||||
|
||||
const mainContainer = view.modelBuilder.flexContainer()
|
||||
.withLayout({
|
||||
flexFlow: 'column',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'absolute'
|
||||
}).component();
|
||||
mainContainer.addItem(container, {
|
||||
CSSStyles: { 'padding-top': '25px', 'padding-left': '5px' }
|
||||
});
|
||||
await view.initializeModel(mainContainer);
|
||||
|
||||
this.refreshMigrations();
|
||||
});
|
||||
}
|
||||
|
||||
private createHeader(view: azdata.ModelView): azdata.FlexContainer {
|
||||
const header = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column',
|
||||
width: maxWidth,
|
||||
}).component();
|
||||
const titleComponent = view.modelBuilder.text().withProps({
|
||||
value: loc.DASHBOARD_TITLE,
|
||||
CSSStyles: {
|
||||
'font-size': '36px',
|
||||
}
|
||||
}).component();
|
||||
const descComponent = view.modelBuilder.text().withProps({
|
||||
value: loc.DASHBOARD_DESCRIPTION,
|
||||
CSSStyles: {
|
||||
'font-size': '12px',
|
||||
}
|
||||
}).component();
|
||||
header.addItems([titleComponent, descComponent], {
|
||||
CSSStyles: {
|
||||
'width': `${maxWidth}px`,
|
||||
'padding-left': '20px'
|
||||
}
|
||||
});
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
private async createTasks(view: azdata.ModelView): Promise<azdata.Component> {
|
||||
const tasksContainer = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'row',
|
||||
width: '100%',
|
||||
height: '50px',
|
||||
}).component();
|
||||
|
||||
const migrateButtonMetadata: IActionMetadata = {
|
||||
title: loc.DASHBOARD_MIGRATE_TASK_BUTTON_TITLE,
|
||||
description: loc.DASHBOARD_MIGRATE_TASK_BUTTON_DESCRIPTION,
|
||||
iconPath: IconPathHelper.sqlMigrationLogo,
|
||||
command: 'sqlmigration.start'
|
||||
};
|
||||
|
||||
const preRequisiteListTitle = view.modelBuilder.text().withProps({
|
||||
value: loc.PRE_REQ_TITLE,
|
||||
CSSStyles: {
|
||||
'font-size': '14px',
|
||||
'padding-left': '15px',
|
||||
'margin-bottom': '-5px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const migrateButton = this.createTaskButton(view, migrateButtonMetadata);
|
||||
|
||||
const points = `• ${loc.PRE_REQ_1}
|
||||
• ${loc.PRE_REQ_2}
|
||||
• ${loc.PRE_REQ_3}`;
|
||||
|
||||
const preRequisiteListElement = view.modelBuilder.text().withProps({
|
||||
value: points,
|
||||
CSSStyles: {
|
||||
'padding-left': '15px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const preRequisiteLearnMoreLink = view.modelBuilder.hyperlink().withProps({
|
||||
label: loc.LEARN_MORE,
|
||||
url: '', //TODO: add link for the pre req document.
|
||||
CSSStyles: {
|
||||
'padding-left': '10px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const preReqContainer = view.modelBuilder.flexContainer().withItems([
|
||||
preRequisiteListTitle,
|
||||
preRequisiteListElement
|
||||
]).withLayout({
|
||||
flexFlow: 'column'
|
||||
}).component();
|
||||
|
||||
preReqContainer.addItem(preRequisiteLearnMoreLink, {
|
||||
CSSStyles: {
|
||||
'padding-left': '10px'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
tasksContainer.addItem(migrateButton, {
|
||||
CSSStyles: {
|
||||
'margin-top': '20px',
|
||||
'padding': '10px'
|
||||
}
|
||||
});
|
||||
tasksContainer.addItems([preReqContainer], {
|
||||
CSSStyles: {
|
||||
'padding': '10px'
|
||||
}
|
||||
});
|
||||
|
||||
return tasksContainer;
|
||||
}
|
||||
|
||||
private createTaskButton(view: azdata.ModelView, taskMetaData: IActionMetadata): azdata.Component {
|
||||
const maxHeight: number = 84;
|
||||
const maxWidth: number = 236;
|
||||
const buttonContainer = view.modelBuilder.button().withProps({
|
||||
buttonType: azdata.ButtonType.Informational,
|
||||
description: taskMetaData.description,
|
||||
height: maxHeight,
|
||||
iconHeight: 32,
|
||||
iconPath: taskMetaData.iconPath,
|
||||
iconWidth: 32,
|
||||
label: taskMetaData.title,
|
||||
title: taskMetaData.title,
|
||||
width: maxWidth,
|
||||
}).component();
|
||||
buttonContainer.onDidClick(async () => {
|
||||
if (taskMetaData.command) {
|
||||
await vscode.commands.executeCommand(taskMetaData.command);
|
||||
}
|
||||
});
|
||||
return view.modelBuilder.divContainer().withItems([buttonContainer]).component();
|
||||
}
|
||||
|
||||
private async refreshMigrations(): Promise<void> {
|
||||
this._migrationStatusCardsContainer.clearItems();
|
||||
const currentConnection = (await azdata.connection.getCurrentConnection());
|
||||
const getMigrations = MigrationLocalStorage.getMigrations(currentConnection);
|
||||
getMigrations.forEach((migration) => {
|
||||
const button = this._view.modelBuilder.button().withProps({
|
||||
label: `Migration to ${migration.targetManagedInstance.name} using controller ${migration.migrationContext.name}`
|
||||
}).component();
|
||||
button.onDidClick(async (e) => {
|
||||
});
|
||||
this._migrationStatusCardsContainer.addItem(
|
||||
button
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private async createFooter(view: azdata.ModelView): Promise<azdata.Component> {
|
||||
const footerContainer = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'row',
|
||||
width: maxWidth,
|
||||
height: '500px',
|
||||
justifyContent: 'flex-start'
|
||||
}).component();
|
||||
const statusContainer = await this.createMigrationStatusContainer(view);
|
||||
const videoLinksContainer = this.createVideoLinks(view);
|
||||
footerContainer.addItem(statusContainer);
|
||||
footerContainer.addItem(videoLinksContainer, {
|
||||
CSSStyles: {
|
||||
'padding-left': '10px',
|
||||
}
|
||||
});
|
||||
|
||||
return footerContainer;
|
||||
}
|
||||
|
||||
private async createMigrationStatusContainer(view: azdata.ModelView): Promise<azdata.FlexContainer> {
|
||||
const statusContainer = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column',
|
||||
width: '400px',
|
||||
height: '280px',
|
||||
justifyContent: 'flex-start',
|
||||
}).withProps({
|
||||
CSSStyles: {
|
||||
'border': '1px solid rgba(0, 0, 0, 0.1)',
|
||||
'padding': '15px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const statusContainerTitle = view.modelBuilder.text().withProps({
|
||||
value: loc.DATABASE_MIGRATION_STATUS,
|
||||
CSSStyles: {
|
||||
'font-size': '18px',
|
||||
'font-weight': 'bold',
|
||||
'margin': '0px',
|
||||
'width': '290px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const viewAllButton = view.modelBuilder.hyperlink().withProps({
|
||||
label: loc.VIEW_ALL,
|
||||
url: ''
|
||||
}).component();
|
||||
|
||||
const refreshButton = view.modelBuilder.hyperlink().withProps({
|
||||
label: loc.REFRESH,
|
||||
url: '',
|
||||
CSSStyles: {
|
||||
'text-align': 'right'
|
||||
}
|
||||
}).component();
|
||||
|
||||
refreshButton.onDidClick((e) => {
|
||||
this.refreshMigrations();
|
||||
});
|
||||
|
||||
const buttonContainer = view.modelBuilder.flexContainer().withLayout({
|
||||
justifyContent: 'flex-end',
|
||||
}).component();
|
||||
|
||||
buttonContainer.addItem(viewAllButton, {
|
||||
flex: 'auto',
|
||||
CSSStyles: {
|
||||
'border-right': '1px solid rgba(0, 0, 0, 0.7)',
|
||||
'width': '40px',
|
||||
}
|
||||
});
|
||||
|
||||
buttonContainer.addItem(refreshButton, {
|
||||
flex: 'auto',
|
||||
CSSStyles: {
|
||||
'margin-left': '5px',
|
||||
'width': '25px'
|
||||
}
|
||||
});
|
||||
|
||||
const header = view.modelBuilder.flexContainer().withItems(
|
||||
[
|
||||
statusContainerTitle,
|
||||
buttonContainer
|
||||
]
|
||||
).withLayout({
|
||||
flexFlow: 'row'
|
||||
}).component();
|
||||
|
||||
|
||||
|
||||
this._migrationStatusCardsContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component();
|
||||
|
||||
statusContainer.addItem(
|
||||
header, {
|
||||
CSSStyles: {
|
||||
'padding': '0px',
|
||||
'padding-right': '5px',
|
||||
'padding-top': '10px',
|
||||
'height': '10px',
|
||||
'margin': '0px'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
statusContainer.addItem(this._migrationStatusCardsContainer, {
|
||||
CSSStyles: {
|
||||
'margin-top': '30px'
|
||||
}
|
||||
});
|
||||
|
||||
return statusContainer;
|
||||
}
|
||||
|
||||
private createVideoLinks(view: azdata.ModelView): azdata.Component {
|
||||
const linksContainer = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column',
|
||||
width: '400px',
|
||||
height: '280px',
|
||||
justifyContent: 'flex-start',
|
||||
}).withProps({
|
||||
CSSStyles: {
|
||||
'border': '1px solid rgba(0, 0, 0, 0.1)',
|
||||
'padding': '15px'
|
||||
}
|
||||
}).component();
|
||||
const titleComponent = view.modelBuilder.text().withProps({
|
||||
value: loc.HELP_TITLE,
|
||||
CSSStyles: {
|
||||
'font-size': '18px',
|
||||
'font-weight': 'bold',
|
||||
'margin': '0px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
linksContainer.addItems([titleComponent], {
|
||||
CSSStyles: {
|
||||
'padding': '0px',
|
||||
'padding-right': '5px',
|
||||
'padding-top': '10px',
|
||||
'height': '10px',
|
||||
'margin': '0px'
|
||||
}
|
||||
});
|
||||
|
||||
const links = [{
|
||||
title: loc.HELP_LINK1_TITLE,
|
||||
description: loc.HELP_LINK1_DESCRIPTION,
|
||||
link: 'www.microsoft.com' //TODO: add proper link over here.
|
||||
}];
|
||||
|
||||
const styles = {
|
||||
'margin-top': '10px',
|
||||
'padding': '10px 10px 10px 0'
|
||||
};
|
||||
linksContainer.addItems(links.map(l => this.createLink(view, l)), {
|
||||
CSSStyles: styles
|
||||
});
|
||||
|
||||
const videosContainer = this.createVideoLinkContainers(view, [
|
||||
{
|
||||
iconPath: IconPathHelper.sqlMiImportHelpThumbnail,
|
||||
description: loc.HELP_VIDEO1_TITLE,
|
||||
link: 'https://www.youtube.com/watch?v=sE99cSoFOHs' //TODO: Fix Video link
|
||||
},
|
||||
{
|
||||
iconPath: IconPathHelper.sqlVmImportHelpThumbnail,
|
||||
description: loc.HELP_VIDEO2_TITLE,
|
||||
link: 'https://www.youtube.com/watch?v=R4GCBoxADyQ' //TODO: Fix video link
|
||||
}
|
||||
]);
|
||||
const viewPanelStyle = {
|
||||
'padding': '10px 5px 10px 10px',
|
||||
'margin-top': '-15px'
|
||||
};
|
||||
linksContainer.addItem(videosContainer, {
|
||||
CSSStyles: viewPanelStyle
|
||||
});
|
||||
|
||||
return linksContainer;
|
||||
}
|
||||
|
||||
private createLink(view: azdata.ModelView, linkMetaData: IActionMetadata): azdata.Component {
|
||||
const maxWidth = 400;
|
||||
const labelsContainer = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column',
|
||||
width: maxWidth,
|
||||
justifyContent: 'flex-start'
|
||||
}).component();
|
||||
const descriptionComponent = view.modelBuilder.text().withProps({
|
||||
value: linkMetaData.description,
|
||||
width: maxWidth,
|
||||
CSSStyles: {
|
||||
'font-size': '12px',
|
||||
'line-height': '16px',
|
||||
'margin': '0px'
|
||||
}
|
||||
}).component();
|
||||
const linkContainer = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'row',
|
||||
width: maxWidth + 10,
|
||||
justifyContent: 'flex-start'
|
||||
}).component();
|
||||
const linkComponent = view.modelBuilder.hyperlink().withProps({
|
||||
label: linkMetaData.title!,
|
||||
url: linkMetaData.link!,
|
||||
showLinkIcon: true,
|
||||
CSSStyles: {
|
||||
'font-size': '14px',
|
||||
'margin': '0px'
|
||||
}
|
||||
}).component();
|
||||
linkContainer.addItem(linkComponent, {
|
||||
CSSStyles: {
|
||||
'font-size': '14px',
|
||||
'line-height': '18px',
|
||||
'padding': '0 5px 0 0',
|
||||
}
|
||||
});
|
||||
labelsContainer.addItems([linkContainer, descriptionComponent], {
|
||||
CSSStyles: {
|
||||
'padding': '5px 0 0 0',
|
||||
}
|
||||
});
|
||||
|
||||
return labelsContainer;
|
||||
}
|
||||
|
||||
private createVideoLinkContainers(view: azdata.ModelView, links: IActionMetadata[]): azdata.Component {
|
||||
const maxWidth = 400;
|
||||
const videosContainer = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'row',
|
||||
width: maxWidth,
|
||||
}).component();
|
||||
|
||||
links.forEach(link => {
|
||||
const videoContainer = this.createVideoLink(view, link);
|
||||
|
||||
videosContainer.addItem(videoContainer);
|
||||
});
|
||||
|
||||
return videosContainer;
|
||||
}
|
||||
|
||||
private createVideoLink(view: azdata.ModelView, linkMetaData: IActionMetadata): azdata.Component {
|
||||
const maxWidth = 150;
|
||||
const videosContainer = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column',
|
||||
width: maxWidth,
|
||||
justifyContent: 'flex-start'
|
||||
}).component();
|
||||
const video1Container = view.modelBuilder.divContainer().withProps({
|
||||
clickable: true,
|
||||
width: maxWidth,
|
||||
height: '100px'
|
||||
}).component();
|
||||
const descriptionComponent = view.modelBuilder.text().withProps({
|
||||
value: linkMetaData.description,
|
||||
width: maxWidth,
|
||||
height: '50px',
|
||||
CSSStyles: {
|
||||
'font-size': '13px',
|
||||
'margin': '0px'
|
||||
}
|
||||
}).component();
|
||||
video1Container.onDidClick(async () => {
|
||||
if (linkMetaData.link) {
|
||||
await vscode.env.openExternal(vscode.Uri.parse(linkMetaData.link));
|
||||
}
|
||||
});
|
||||
videosContainer.addItem(video1Container, {
|
||||
CSSStyles: {
|
||||
'background-image': `url(${vscode.Uri.file(<string>linkMetaData.iconPath?.light)})`,
|
||||
'background-repeat': 'no-repeat',
|
||||
'background-position': 'top',
|
||||
'width': `${maxWidth}px`,
|
||||
'height': '104px',
|
||||
'background-size': `${maxWidth}px 120px`
|
||||
}
|
||||
});
|
||||
videosContainer.addItem(descriptionComponent);
|
||||
return videosContainer;
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,13 @@
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import { createMigrationController, getMigrationControllerRegions, getMigrationController, getResourceGroups, getMigrationControllerAuthKeys, getMigrationControllerMonitoringData } from '../api/azure';
|
||||
import { MigrationStateModel } from '../models/stateMachine';
|
||||
import * as constants from '../models/strings';
|
||||
import { createMigrationController, getMigrationControllerRegions, getMigrationController, getResourceGroups, getMigrationControllerAuthKeys, getMigrationControllerMonitoringData } from '../../api/azure';
|
||||
import { MigrationStateModel } from '../../models/stateMachine';
|
||||
import * as constants from '../../models/strings';
|
||||
import * as os from 'os';
|
||||
import { azureResource } from 'azureResource';
|
||||
import { IntergrationRuntimePage } from './integrationRuntimePage';
|
||||
import { IconPathHelper } from '../constants/iconPathHelper';
|
||||
import { IntergrationRuntimePage } from '../../wizard/integrationRuntimePage';
|
||||
import { IconPathHelper } from '../../constants/iconPathHelper';
|
||||
|
||||
export class CreateMigrationControllerDialog {
|
||||
|
||||
@@ -464,7 +464,7 @@ export class CreateMigrationControllerDialog {
|
||||
value: constants.CONTROLLER_KEY1_LABEL
|
||||
},
|
||||
{
|
||||
value: keys.keyName1
|
||||
value: keys.authKey1
|
||||
},
|
||||
{
|
||||
value: this._copyKey1Button
|
||||
@@ -478,7 +478,7 @@ export class CreateMigrationControllerDialog {
|
||||
value: constants.CONTROLLER_KEY2_LABEL
|
||||
},
|
||||
{
|
||||
value: keys.keyName2
|
||||
value: keys.authKey2
|
||||
},
|
||||
{
|
||||
value: this._copyKey2Button
|
||||
@@ -11,12 +11,15 @@ import { promises as fs } from 'fs';
|
||||
import * as loc from './models/strings';
|
||||
import { MigrationNotebookInfo, NotebookPathHelper } from './constants/notebookPathHelper';
|
||||
import { IconPathHelper } from './constants/iconPathHelper';
|
||||
import { DashboardWidget } from './dashboard/sqlServerDashboard';
|
||||
import { MigrationLocalStorage } from './models/migrationLocalStorage';
|
||||
|
||||
class SQLMigration {
|
||||
|
||||
constructor(private readonly context: vscode.ExtensionContext) {
|
||||
NotebookPathHelper.setExtensionContext(context);
|
||||
IconPathHelper.setExtensionContext(context);
|
||||
MigrationLocalStorage.setExtensionContext(context);
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
@@ -83,6 +86,8 @@ let sqlMigration: SQLMigration;
|
||||
export async function activate(context: vscode.ExtensionContext) {
|
||||
sqlMigration = new SQLMigration(context);
|
||||
await sqlMigration.registerCommands();
|
||||
let widget = new DashboardWidget();
|
||||
widget.register();
|
||||
}
|
||||
|
||||
export function deactivate(): void {
|
||||
|
||||
@@ -23,7 +23,7 @@ export class MigrationLocalStorage {
|
||||
const migrationMementos: MigrationContext[] = this.context.globalState.get(this.mementoToken) || [];
|
||||
|
||||
dataBaseMigrations = migrationMementos.filter((memento) => {
|
||||
return memento.connection.serverName === connectionProfile.serverName;
|
||||
return memento.sourceConnectionProfile.serverName === connectionProfile.serverName;
|
||||
}).map((memento) => {
|
||||
return memento;
|
||||
});
|
||||
@@ -35,13 +35,13 @@ export class MigrationLocalStorage {
|
||||
return dataBaseMigrations;
|
||||
}
|
||||
|
||||
public static saveMigration(connection: azdata.connection.ConnectionProfile, migration: DatabaseMigration, targetMI: SqlManagedInstance, azureAccount: azdata.Account, subscription: azureResource.AzureResourceSubscription): void {
|
||||
public static saveMigration(connectionProfile: azdata.connection.ConnectionProfile, migrationContext: DatabaseMigration, targetMI: SqlManagedInstance, azureAccount: azdata.Account, subscription: azureResource.AzureResourceSubscription): void {
|
||||
try {
|
||||
const migrationMementos: MigrationContext[] = this.context.globalState.get(this.mementoToken) || [];
|
||||
migrationMementos.push({
|
||||
connection: connection,
|
||||
migration: migration,
|
||||
targetMI: targetMI,
|
||||
sourceConnectionProfile: connectionProfile,
|
||||
migrationContext: migrationContext,
|
||||
targetManagedInstance: targetMI,
|
||||
subscription: subscription,
|
||||
azureAccount: azureAccount
|
||||
});
|
||||
@@ -57,9 +57,9 @@ export class MigrationLocalStorage {
|
||||
}
|
||||
|
||||
export interface MigrationContext {
|
||||
connection: azdata.connection.ConnectionProfile,
|
||||
migration: DatabaseMigration,
|
||||
targetMI: SqlManagedInstance,
|
||||
sourceConnectionProfile: azdata.connection.ConnectionProfile,
|
||||
migrationContext: DatabaseMigration,
|
||||
targetManagedInstance: SqlManagedInstance,
|
||||
azureAccount: azdata.Account,
|
||||
subscription: azureResource.AzureResourceSubscription
|
||||
}
|
||||
|
||||
@@ -453,7 +453,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
Scope: this._targetManagedInstance.id
|
||||
}
|
||||
};
|
||||
console.log(requestBody);
|
||||
|
||||
const response = await startDatabaseMigration(
|
||||
this.azureAccount,
|
||||
this._targetSubscription,
|
||||
@@ -464,9 +464,8 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
||||
requestBody
|
||||
);
|
||||
|
||||
console.log(response);
|
||||
if (!response.error) {
|
||||
MigrationLocalStorage.saveMigration(currentConnection!, response, this._targetManagedInstance, this.azureAccount, this._targetSubscription);
|
||||
if (response.status === 201) {
|
||||
MigrationLocalStorage.saveMigration(currentConnection!, response.databaseMigration, this._targetManagedInstance, this.azureAccount, this._targetSubscription);
|
||||
}
|
||||
|
||||
vscode.window.showInformationMessage(constants.MIGRATION_STARTED);
|
||||
|
||||
@@ -149,7 +149,7 @@ export const CANCEL = localize('sql.migration.cancel', "Cancel");
|
||||
export const TYPE = localize('sql.migration.type', "Type");
|
||||
export const PATH = localize('sql.migration.path', "Path");
|
||||
export const USER_ACCOUNT = localize('sql.migration.path.user.account', "User Account");
|
||||
|
||||
export const VIEW_ALL = localize('sql.migration.view.all', "View All");
|
||||
|
||||
//Summary Page
|
||||
export const SUMMARY_PAGE_TITLE = localize('sql.migration.summary.page.title', "Summary");
|
||||
@@ -171,3 +171,20 @@ export const NOTEBOOK_QUICK_PICK_PLACEHOLDER = localize('sql.migration.quick.pic
|
||||
export const NOTEBOOK_INLINE_MIGRATION_TITLE = localize('sql.migration.inline.migration.notebook.title', "Inline migration");
|
||||
export const NOTEBOOK_SQL_MIGRATION_ASSESSMENT_TITLE = localize('sql.migration.sql.assessment.notebook.title', "SQL migration assessment");
|
||||
export const NOTEBOOK_OPEN_ERROR = localize('sql.migration.notebook.open.error', "Error opening migration notebook");
|
||||
|
||||
// Dashboard
|
||||
export const DASHBOARD_TITLE = localize('sql.migration.dashboard.title', "Azure SQL Migration");
|
||||
export const DASHBOARD_DESCRIPTION = localize('sql.migration.dashboard.description', "Determine the migration readiness of your SQL Server instances, identify a recommended Azure SQL target, and complete the migration of your SQL Server instance to Azure SQL Managed Instance or SQL Server on Azure Virtual Machines.");
|
||||
export const DASHBOARD_MIGRATE_TASK_BUTTON_TITLE = localize('sql.migration.dashboard.migrate.task.button', "Migrate to Azure SQL");
|
||||
export const DASHBOARD_MIGRATE_TASK_BUTTON_DESCRIPTION = localize('sql.migration.dashboard.migrate.task.button.description', "Migrate SQL Server instance to Azure SQL.");
|
||||
export const DATABASE_MIGRATION_STATUS = localize('sql.migration.database.migration.status', "Database Migration Status");
|
||||
export const HELP_VIDEO1_TITLE = localize('sql.migration.dashboard.video1.title', "Migrate to SQL Server to SQL Managed Instance");
|
||||
export const HELP_VIDEO2_TITLE = localize('sql.migration.dashboard.video2.title', "Migrate to SQL Server to SQL Virtual Machine");
|
||||
export const HELP_LINK1_TITLE = localize('sql.migration.dashboard.link1.title', "Migrating your SQL Server to cloud");
|
||||
export const HELP_LINK1_DESCRIPTION = localize('sql.migration.dashboard.link1.description', "Lorem ipsum dolor sit amet, consectetur adipi. Lorem ipsum dolor sit amet, consectetur adipi. Lorem ipsum.");
|
||||
export const HELP_TITLE = localize('sql.migration.dashboard.help.title', "Help Articles and Video Links");
|
||||
export const PRE_REQ_TITLE = localize('sql.migration.pre.req.title', "Things you need before starting migration:");
|
||||
export const PRE_REQ_1 = localize('sql.migration.pre.req.1', "Azure account details");
|
||||
export const PRE_REQ_2 = localize('sql.migration.pre.req.2', "Azure SQL Managed Instance or SQL Server on Azure Virtual Machine");
|
||||
export const PRE_REQ_3 = localize('sql.migration.pre.req.3', "Backup location details");
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as azdata from 'azdata';
|
||||
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
|
||||
import { CreateMigrationControllerDialog } from './createMigrationControllerDialog';
|
||||
import { CreateMigrationControllerDialog } from '../dialog/createMigrationDialog/createMigrationControllerDialog';
|
||||
import * as constants from '../models/strings';
|
||||
import * as os from 'os';
|
||||
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
||||
|
||||
Reference in New Issue
Block a user