mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-23 11:01:38 -05:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6783aa6967 | ||
|
|
ccbc2f74fe | ||
|
|
d7283a6e56 | ||
|
|
0684040d34 | ||
|
|
625eb00be2 | ||
|
|
c5a27a89f3 | ||
|
|
b9a7d5e4bd | ||
|
|
f876c00ca1 | ||
|
|
83ae789aa0 | ||
|
|
6fe4d0a561 | ||
|
|
2eaec9f41d | ||
|
|
2edafe50bb | ||
|
|
24c5686bd6 | ||
|
|
1731aeffbe | ||
|
|
b35ff6451a | ||
|
|
07aa256f4c | ||
|
|
473764de9a |
@@ -153,27 +153,27 @@ export async function getLocations(appContext: AppContext, account?: AzureAccoun
|
|||||||
result.errors.push(error);
|
result.errors.push(error);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
await Promise.all(account.properties.tenants.map(async (tenant: { id: string; }) => {
|
|
||||||
try {
|
try {
|
||||||
const path = `/subscriptions/${subscription.id}/locations?api-version=2020-01-01`;
|
const path = `/subscriptions/${subscription.id}/locations?api-version=2020-01-01`;
|
||||||
const response = await makeHttpRequest(account, subscription, path, HttpRequestMethod.GET, undefined, ignoreErrors);
|
const response = await makeHttpRequest(account, subscription, path, HttpRequestMethod.GET, undefined, ignoreErrors);
|
||||||
result.locations.push(...response.response.data.value);
|
result.locations.push(...response.response.data.value);
|
||||||
result.errors.push(...response.errors);
|
result.errors.push(...response.errors);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const error = new Error(localize('azure.accounts.getLocations.queryError', "Error fetching locations for account {0} ({1}) subscription {2} ({3}) tenant {4} : {5}",
|
const error = new Error(localize('azure.accounts.getLocations.queryError', "Error fetching locations for account {0} ({1}) subscription {2} ({3}) tenant {4} : {5}",
|
||||||
account.displayInfo.displayName,
|
account.displayInfo.displayName,
|
||||||
account.displayInfo.userId,
|
account.displayInfo.userId,
|
||||||
subscription.id,
|
subscription.id,
|
||||||
subscription.name,
|
subscription.name,
|
||||||
tenant.id,
|
account.properties.tenants[0].id,
|
||||||
err instanceof Error ? err.message : err));
|
err instanceof Error ? err.message : err));
|
||||||
console.warn(error);
|
console.warn(error);
|
||||||
if (!ignoreErrors) {
|
if (!ignoreErrors) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
|
||||||
result.errors.push(error);
|
|
||||||
}
|
}
|
||||||
}));
|
result.errors.push(error);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,26 +89,26 @@ export class BookTocManager implements IBookTocManager {
|
|||||||
let toc: JupyterBookSection[] = [];
|
let toc: JupyterBookSection[] = [];
|
||||||
for (const content of contents) {
|
for (const content of contents) {
|
||||||
try {
|
try {
|
||||||
const contentStat = (await fs.promises.stat(path.join(directory, content)));
|
const contentStat = (await fs.promises.stat(path.posix.join(directory, content)));
|
||||||
const parsedFile = path.parse(content);
|
const parsedFile = path.parse(content);
|
||||||
if (contentStat.isFile() && allowedFileExtensions.includes(parsedFile.ext)) {
|
if (contentStat.isFile() && allowedFileExtensions.includes(parsedFile.ext)) {
|
||||||
let filePath = directory === rootDirectory ? path.posix.join(path.posix.sep, parsedFile.name) : path.posix.join(path.posix.sep, path.relative(rootDirectory, directory), parsedFile.name);
|
let filePath = directory === rootDirectory ? path.posix.join(path.posix.sep, parsedFile.name) : path.posix.join(path.posix.sep, path.posix.relative(rootDirectory, directory), parsedFile.name);
|
||||||
const section: JupyterBookSection = {
|
const section: JupyterBookSection = {
|
||||||
title: parsedFile.name,
|
title: parsedFile.name,
|
||||||
file: filePath
|
file: filePath
|
||||||
};
|
};
|
||||||
toc.push(section);
|
toc.push(section);
|
||||||
} else if (contentStat.isDirectory()) {
|
} else if (contentStat.isDirectory()) {
|
||||||
let files = await fs.promises.readdir(path.join(directory, content));
|
let files = await fs.promises.readdir(path.posix.join(directory, content));
|
||||||
let initFile = this.getInitFile(files);
|
let initFile = this.getInitFile(files);
|
||||||
let filePath = directory === rootDirectory ? path.posix.join(path.posix.sep, parsedFile.name, initFile.name) : path.posix.join(path.posix.sep, path.relative(rootDirectory, directory), parsedFile.name, initFile.name);
|
let filePath = directory === rootDirectory ? path.posix.join(path.posix.sep, parsedFile.name, initFile.name) : path.posix.join(path.posix.sep, path.posix.relative(rootDirectory, directory), parsedFile.name, initFile.name);
|
||||||
let section: JupyterBookSection = {};
|
let section: JupyterBookSection = {};
|
||||||
section = {
|
section = {
|
||||||
title: parsedFile.name,
|
title: parsedFile.name,
|
||||||
file: filePath,
|
file: filePath,
|
||||||
expand_sections: true,
|
expand_sections: true,
|
||||||
numbered: false,
|
numbered: false,
|
||||||
sections: await this.createTocFromDir(files, path.join(directory, content), rootDirectory)
|
sections: await this.createTocFromDir(files, path.posix.join(directory, content), rootDirectory)
|
||||||
};
|
};
|
||||||
toc.push(section);
|
toc.push(section);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ describe('BookTocManagerTests', function () {
|
|||||||
let rootFolderPath: string;
|
let rootFolderPath: string;
|
||||||
let root2FolderPath: string;
|
let root2FolderPath: string;
|
||||||
const subfolder = 'Subfolder';
|
const subfolder = 'Subfolder';
|
||||||
|
const subfolder2 = 'Subfolder2';
|
||||||
|
|
||||||
afterEach(function (): void {
|
afterEach(function (): void {
|
||||||
sinon.restore();
|
sinon.restore();
|
||||||
@@ -94,7 +95,7 @@ describe('BookTocManagerTests', function () {
|
|||||||
rootFolderPath = path.join(os.tmpdir(), `BookTestData_${uuid.v4()}`);
|
rootFolderPath = path.join(os.tmpdir(), `BookTestData_${uuid.v4()}`);
|
||||||
bookFolderPath = path.join(os.tmpdir(), `BookTestData_${uuid.v4()}`);
|
bookFolderPath = path.join(os.tmpdir(), `BookTestData_${uuid.v4()}`);
|
||||||
root2FolderPath = path.join(os.tmpdir(), `BookTestData_${uuid.v4()}`);
|
root2FolderPath = path.join(os.tmpdir(), `BookTestData_${uuid.v4()}`);
|
||||||
notebooks = ['notebook1.ipynb', 'notebook2.ipynb', 'notebook3.ipynb', 'index.md'];
|
notebooks = ['notebook1.ipynb', 'notebook2.ipynb', 'notebook3.ipynb', 'index.md', 'notebook4.ipynb', 'notebook5.ipynb'];
|
||||||
|
|
||||||
await fs.mkdir(rootFolderPath);
|
await fs.mkdir(rootFolderPath);
|
||||||
await fs.writeFile(path.join(rootFolderPath, notebooks[0]), '');
|
await fs.writeFile(path.join(rootFolderPath, notebooks[0]), '');
|
||||||
@@ -104,6 +105,8 @@ describe('BookTocManagerTests', function () {
|
|||||||
|
|
||||||
await fs.mkdir(root2FolderPath);
|
await fs.mkdir(root2FolderPath);
|
||||||
await fs.mkdir(path.join(root2FolderPath, subfolder));
|
await fs.mkdir(path.join(root2FolderPath, subfolder));
|
||||||
|
await fs.mkdir(path.join(root2FolderPath, subfolder, subfolder2));
|
||||||
|
|
||||||
await fs.writeFile(path.join(root2FolderPath, notebooks[0]), '');
|
await fs.writeFile(path.join(root2FolderPath, notebooks[0]), '');
|
||||||
await fs.writeFile(path.join(root2FolderPath, subfolder, notebooks[1]), '');
|
await fs.writeFile(path.join(root2FolderPath, subfolder, notebooks[1]), '');
|
||||||
await fs.writeFile(path.join(root2FolderPath, subfolder, notebooks[2]), '');
|
await fs.writeFile(path.join(root2FolderPath, subfolder, notebooks[2]), '');
|
||||||
@@ -145,6 +148,51 @@ describe('BookTocManagerTests', function () {
|
|||||||
should(bookTocManager.tableofContents.length).be.equal(4);
|
should(bookTocManager.tableofContents.length).be.equal(4);
|
||||||
should(listFiles.length).be.equal(7);
|
should(listFiles.length).be.equal(7);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it ('should create a table of contents with sections if folder contains subfolders', async () => {
|
||||||
|
await fs.writeFile(path.join(root2FolderPath, subfolder, subfolder2, notebooks[4]), '');
|
||||||
|
await fs.writeFile(path.join(root2FolderPath, subfolder, subfolder2, notebooks[5]), '');
|
||||||
|
|
||||||
|
let bookTocManager: BookTocManager = new BookTocManager();
|
||||||
|
await bookTocManager.createBook(bookFolderPath, root2FolderPath);
|
||||||
|
let listFiles = await fs.promises.readdir(bookFolderPath);
|
||||||
|
should(bookTocManager.tableofContents.length).be.equal(3);
|
||||||
|
should(listFiles.length).be.equal(5);
|
||||||
|
|
||||||
|
let expectedSubSections: IJupyterBookSectionV2[] = [{
|
||||||
|
title: 'notebook4',
|
||||||
|
file: path.posix.join(path.posix.sep, subfolder, subfolder2, 'notebook4')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'notebook5',
|
||||||
|
file: path.posix.join(path.posix.sep, subfolder, subfolder2, 'notebook5')
|
||||||
|
}];
|
||||||
|
|
||||||
|
let expectedSection: IJupyterBookSectionV2[] = [{
|
||||||
|
title: 'index',
|
||||||
|
file: path.posix.join(path.posix.sep, subfolder, 'index')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'notebook2',
|
||||||
|
file: path.posix.join(path.posix.sep, subfolder, 'notebook2')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'notebook3',
|
||||||
|
file: path.posix.join(path.posix.sep, subfolder, 'notebook3')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Subfolder2',
|
||||||
|
file: path.posix.join(path.posix.sep, subfolder, subfolder2, 'notebook4'),
|
||||||
|
sections : expectedSubSections
|
||||||
|
}];
|
||||||
|
|
||||||
|
const index = bookTocManager.tableofContents.findIndex(entry => entry.file === path.posix.join(path.posix.sep, subfolder, 'index'));
|
||||||
|
should(index).not.be.equal(-1, 'Should find a section with the Subfolder entry');
|
||||||
|
if (index !== -1) {
|
||||||
|
let subsection = bookTocManager.tableofContents[index].sections.find(entry => entry.file === path.posix.join(path.posix.sep, subfolder, subfolder2, 'notebook4'));
|
||||||
|
should(equalSections(subsection, expectedSection[3])).be.true('Should find a subsection with the subfolder2 inside the subfolder');
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('EditingBooks', () => {
|
describe('EditingBooks', () => {
|
||||||
|
|||||||
@@ -45,6 +45,10 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "%sqlDatabaseProjects.netCoreDoNotAsk%"
|
"description": "%sqlDatabaseProjects.netCoreDoNotAsk%"
|
||||||
},
|
},
|
||||||
|
"sqlDatabaseProjects.netCoreDowngradeDoNotShow": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "%sqlDatabaseProjects.netCoreDowngradeDoNotShow%"
|
||||||
|
},
|
||||||
"sqlDatabaseProjects.nodejsDoNotAsk": {
|
"sqlDatabaseProjects.nodejsDoNotAsk": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "%sqlDatabaseProjects.nodejsDoNotAsk%"
|
"description": "%sqlDatabaseProjects.nodejsDoNotAsk%"
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
"sqlDatabaseProjects.Settings": "Database Projects",
|
"sqlDatabaseProjects.Settings": "Database Projects",
|
||||||
"sqlDatabaseProjects.netCoreInstallLocation": "Full path to .NET Core SDK on the machine.",
|
"sqlDatabaseProjects.netCoreInstallLocation": "Full path to .NET Core SDK on the machine.",
|
||||||
"sqlDatabaseProjects.netCoreDoNotAsk": "Whether to prompt the user to install .NET Core when not detected.",
|
"sqlDatabaseProjects.netCoreDoNotAsk": "Whether to prompt the user to install .NET Core when not detected.",
|
||||||
|
"sqlDatabaseProjects.netCoreDowngradeDoNotShow": "Whether to prompt the user to set .NET SDK version when a newer unsupported version is detected.",
|
||||||
"sqlDatabaseProjects.nodejsDoNotAsk": "Whether to prompt the user to install Node.js when not detected.",
|
"sqlDatabaseProjects.nodejsDoNotAsk": "Whether to prompt the user to install Node.js when not detected.",
|
||||||
"sqlDatabaseProjects.autorestSqlVersion": "Which version of Autorest.Sql to use from NPM. Latest will be used if not set.",
|
"sqlDatabaseProjects.autorestSqlVersion": "Which version of Autorest.Sql to use from NPM. Latest will be used if not set.",
|
||||||
"sqlDatabaseProjects.welcome": "No database projects currently open.\n[New Project](command:sqlDatabaseProjects.new)\n[Open Project](command:sqlDatabaseProjects.open)\n[Create Project From Database](command:sqlDatabaseProjects.importDatabase)",
|
"sqlDatabaseProjects.welcome": "No database projects currently open.\n[New Project](command:sqlDatabaseProjects.new)\n[Open Project](command:sqlDatabaseProjects.open)\n[Create Project From Database](command:sqlDatabaseProjects.importDatabase)",
|
||||||
|
|||||||
@@ -115,8 +115,7 @@ export const defaultUser = localize('default', "default");
|
|||||||
export const selectProfileToUse = localize('selectProfileToUse', "Select publish profile to load");
|
export const selectProfileToUse = localize('selectProfileToUse', "Select publish profile to load");
|
||||||
export const selectProfile = localize('selectProfile', "Select Profile");
|
export const selectProfile = localize('selectProfile', "Select Profile");
|
||||||
export const dontUseProfile = localize('dontUseProfile', "Don't use profile");
|
export const dontUseProfile = localize('dontUseProfile', "Don't use profile");
|
||||||
export const browseForProfile = localize('browseForProfile', "Browse for profile");
|
export const browseForProfileWithIcon = `$(folder) ${localize('browseForProfile', "Browse for profile")}`;
|
||||||
export const browseForProfileWithIcon = `$(folder) ${browseForProfile}`;
|
|
||||||
export const chooseAction = localize('chooseAction', "Choose action");
|
export const chooseAction = localize('chooseAction', "Choose action");
|
||||||
export const chooseSqlcmdVarsToModify = localize('chooseSqlcmdVarsToModify', "Choose SQLCMD variables to modify");
|
export const chooseSqlcmdVarsToModify = localize('chooseSqlcmdVarsToModify', "Choose SQLCMD variables to modify");
|
||||||
export const enterNewValueForVar = (varName: string) => localize('enterNewValueForVar', "Enter new value for variable '{0}'", varName);
|
export const enterNewValueForVar = (varName: string) => localize('enterNewValueForVar', "Enter new value for variable '{0}'", varName);
|
||||||
@@ -315,12 +314,14 @@ export const postDeployScriptFriendlyName = localize('postDeployScriptFriendlyNa
|
|||||||
|
|
||||||
export const NetCoreInstallationConfirmation: string = localize('sqlDatabaseProjects.NetCoreInstallationConfirmation', "The .NET Core SDK cannot be located. Project build will not work. Please install .NET Core SDK version 3.1 or update the .NET Core SDK location in settings if already installed.");
|
export const NetCoreInstallationConfirmation: string = localize('sqlDatabaseProjects.NetCoreInstallationConfirmation', "The .NET Core SDK cannot be located. Project build will not work. Please install .NET Core SDK version 3.1 or update the .NET Core SDK location in settings if already installed.");
|
||||||
export function NetCoreSupportedVersionInstallationConfirmation(installedVersion: string) { return localize('sqlDatabaseProjects.NetCoreSupportedVersionInstallationConfirmation', "Currently installed .NET Core SDK version is {0}, which is not supported. Project build will not work. Please install .NET Core SDK version 3.1 or update the .NET Core SDK supported version location in settings if already installed.", installedVersion); }
|
export function NetCoreSupportedVersionInstallationConfirmation(installedVersion: string) { return localize('sqlDatabaseProjects.NetCoreSupportedVersionInstallationConfirmation', "Currently installed .NET Core SDK version is {0}, which is not supported. Project build will not work. Please install .NET Core SDK version 3.1 or update the .NET Core SDK supported version location in settings if already installed.", installedVersion); }
|
||||||
|
export function NetCoreVersionDowngradeConfirmation(installedVersion: string) { return localize('sqlDatabaseProjects.NetCoreVersionDowngradeConfirmation', "Installed .NET SDK version {0} is newer than the currently supported versions. Project build will not work. Please install .NET Core SDK version 3.1 and include a global.json in the project folder specifying the SDK version to use. [More Information](https://docs.microsoft.com/dotnet/core/versions/selection)", installedVersion); }
|
||||||
export const UpdateNetCoreLocation: string = localize('sqlDatabaseProjects.UpdateNetCoreLocation', "Update Location");
|
export const UpdateNetCoreLocation: string = localize('sqlDatabaseProjects.UpdateNetCoreLocation', "Update Location");
|
||||||
export const projectsOutputChannel = localize('sqlDatabaseProjects.outputChannel', "Database Projects");
|
export const projectsOutputChannel = localize('sqlDatabaseProjects.outputChannel', "Database Projects");
|
||||||
|
|
||||||
// Prompt buttons
|
// Prompt buttons
|
||||||
export const Install: string = localize('sqlDatabaseProjects.Install', "Install");
|
export const Install: string = localize('sqlDatabaseProjects.Install', "Install");
|
||||||
export const DoNotAskAgain: string = localize('sqlDatabaseProjects.doNotAskAgain', "Don't Ask Again");
|
export const DoNotAskAgain: string = localize('sqlDatabaseProjects.doNotAskAgain', "Don't Ask Again");
|
||||||
|
export const DoNotShowAgain: string = localize('sqlDatabaseProjects.doNotShowAgain', "Don't Show Again");
|
||||||
|
|
||||||
// SqlProj file XML names
|
// SqlProj file XML names
|
||||||
export const ItemGroup = 'ItemGroup';
|
export const ItemGroup = 'ItemGroup';
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export async function getPublishDatabaseSettings(project: Project, promptForConn
|
|||||||
reject();
|
reject();
|
||||||
});
|
});
|
||||||
quickPick.onDidChangeSelection(async items => {
|
quickPick.onDidChangeSelection(async items => {
|
||||||
if (items[0].label === constants.browseForProfile) {
|
if (items[0].label === constants.browseForProfileWithIcon) {
|
||||||
const locations = await promptForPublishProfile(project.projectFolderPath);
|
const locations = await promptForPublishProfile(project.projectFolderPath);
|
||||||
if (!locations) {
|
if (!locations) {
|
||||||
// Clear items so that this event will trigger again if they select the same item
|
// Clear items so that this event will trigger again if they select the same item
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import * as semver from 'semver';
|
|||||||
import { isNullOrUndefined } from 'util';
|
import { isNullOrUndefined } from 'util';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { DoNotAskAgain, Install, NetCoreInstallationConfirmation, NetCoreSupportedVersionInstallationConfirmation, UpdateNetCoreLocation } from '../common/constants';
|
import { DoNotAskAgain, DoNotShowAgain, Install, NetCoreInstallationConfirmation, NetCoreSupportedVersionInstallationConfirmation, NetCoreVersionDowngradeConfirmation, UpdateNetCoreLocation } from '../common/constants';
|
||||||
import * as utils from '../common/utils';
|
import * as utils from '../common/utils';
|
||||||
import { ShellCommandOptions, ShellExecutionHelper } from './shellExecutionHelper';
|
import { ShellCommandOptions, ShellExecutionHelper } from './shellExecutionHelper';
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
@@ -19,16 +19,19 @@ const localize = nls.loadMessageBundle();
|
|||||||
export const DBProjectConfigurationKey: string = 'sqlDatabaseProjects';
|
export const DBProjectConfigurationKey: string = 'sqlDatabaseProjects';
|
||||||
export const NetCoreInstallLocationKey: string = 'netCoreSDKLocation';
|
export const NetCoreInstallLocationKey: string = 'netCoreSDKLocation';
|
||||||
export const NetCoreDoNotAskAgainKey: string = 'netCoreDoNotAsk';
|
export const NetCoreDoNotAskAgainKey: string = 'netCoreDoNotAsk';
|
||||||
|
export const NetCoreDowngradeDoNotShowAgainKey: string = 'netCoreDowngradeDoNotShow';
|
||||||
export const NetCoreNonWindowsDefaultPath = '/usr/local/share';
|
export const NetCoreNonWindowsDefaultPath = '/usr/local/share';
|
||||||
export const winPlatform: string = 'win32';
|
export const winPlatform: string = 'win32';
|
||||||
export const macPlatform: string = 'darwin';
|
export const macPlatform: string = 'darwin';
|
||||||
export const linuxPlatform: string = 'linux';
|
export const linuxPlatform: string = 'linux';
|
||||||
export const minSupportedNetCoreVersion: string = '3.1.0';
|
export const minSupportedNetCoreVersion: string = '3.1.0';
|
||||||
|
export const maxSupportedNetCoreVersionCutoff: string = '6.0.0'; // un-set this to allow latest
|
||||||
|
|
||||||
export const enum netCoreInstallState {
|
export const enum netCoreInstallState {
|
||||||
netCoreNotPresent,
|
netCoreNotPresent,
|
||||||
netCoreVersionNotSupported,
|
netCoreVersionNotSupported,
|
||||||
netCoreVersionSupported
|
netCoreVersionSupported,
|
||||||
|
netCoreVersionTooHigh
|
||||||
}
|
}
|
||||||
|
|
||||||
const dotnet = os.platform() === 'win32' ? 'dotnet.exe' : 'dotnet';
|
const dotnet = os.platform() === 'win32' ? 'dotnet.exe' : 'dotnet';
|
||||||
@@ -46,8 +49,10 @@ export class NetCoreTool extends ShellExecutionHelper {
|
|||||||
*/
|
*/
|
||||||
public async findOrInstallNetCore(): Promise<boolean> {
|
public async findOrInstallNetCore(): Promise<boolean> {
|
||||||
if ((!this.isNetCoreInstallationPresent || !await this.isNetCoreVersionSupported())) {
|
if ((!this.isNetCoreInstallationPresent || !await this.isNetCoreVersionSupported())) {
|
||||||
if (vscode.workspace.getConfiguration(DBProjectConfigurationKey)[NetCoreDoNotAskAgainKey] !== true) {
|
if (this.netCoreInstallState === netCoreInstallState.netCoreVersionSupported && vscode.workspace.getConfiguration(DBProjectConfigurationKey)[NetCoreDoNotAskAgainKey] !== true) {
|
||||||
void this.showInstallDialog(); // Removing await so that Build and extension load process doesn't wait on user input
|
void this.showInstallDialog(); // Removing await so that Build and extension load process doesn't wait on user input
|
||||||
|
} else if (this.netCoreInstallState === netCoreInstallState.netCoreVersionTooHigh && vscode.workspace.getConfiguration(DBProjectConfigurationKey)[NetCoreDowngradeDoNotShowAgainKey] !== true) {
|
||||||
|
void this.showDowngradeDialog();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -80,6 +85,15 @@ export class NetCoreTool extends ShellExecutionHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async showDowngradeDialog(): Promise<void> {
|
||||||
|
const result = await vscode.window.showErrorMessage(NetCoreVersionDowngradeConfirmation(this.netCoreSdkInstalledVersion!), DoNotShowAgain);
|
||||||
|
|
||||||
|
if (result === DoNotShowAgain) {
|
||||||
|
const config = vscode.workspace.getConfiguration(DBProjectConfigurationKey);
|
||||||
|
await config.update(NetCoreDowngradeDoNotShowAgainKey, true, vscode.ConfigurationTarget.Global);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private get isNetCoreInstallationPresent(): boolean {
|
private get isNetCoreInstallationPresent(): boolean {
|
||||||
const netCoreInstallationPresent = (!isNullOrUndefined(this.netcoreInstallLocation) && fs.existsSync(this.netcoreInstallLocation));
|
const netCoreInstallationPresent = (!isNullOrUndefined(this.netcoreInstallLocation) && fs.existsSync(this.netcoreInstallLocation));
|
||||||
if (!netCoreInstallationPresent) {
|
if (!netCoreInstallationPresent) {
|
||||||
@@ -122,8 +136,8 @@ export class NetCoreTool extends ShellExecutionHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function checks if the installed dotnet version is atleast minSupportedNetCoreVersion.
|
* This function checks if the installed dotnet version is between minSupportedNetCoreVersion (inclusive) and maxSupportedNetCoreVersionCutoff (exclusive).
|
||||||
* Versions lower than minSupportedNetCoreVersion aren't supported for building projects.
|
* When maxSupportedNetCoreVersionCutoff is not set, the latest dotnet version is assumed to be supported and only the min version is checked.
|
||||||
* Returns: True if installed dotnet version is supported, false otherwise.
|
* Returns: True if installed dotnet version is supported, false otherwise.
|
||||||
* Undefined if dotnet isn't installed.
|
* Undefined if dotnet isn't installed.
|
||||||
*/
|
*/
|
||||||
@@ -131,7 +145,7 @@ export class NetCoreTool extends ShellExecutionHelper {
|
|||||||
try {
|
try {
|
||||||
const spawn = child_process.spawn;
|
const spawn = child_process.spawn;
|
||||||
let child: child_process.ChildProcessWithoutNullStreams;
|
let child: child_process.ChildProcessWithoutNullStreams;
|
||||||
let isSupported: boolean | undefined = undefined;
|
let installState: netCoreInstallState = netCoreInstallState.netCoreVersionSupported;
|
||||||
const stdoutBuffers: Buffer[] = [];
|
const stdoutBuffers: Buffer[] = [];
|
||||||
|
|
||||||
child = spawn('dotnet --version', [], {
|
child = spawn('dotnet --version', [], {
|
||||||
@@ -145,10 +159,21 @@ export class NetCoreTool extends ShellExecutionHelper {
|
|||||||
this.netCoreSdkInstalledVersion = Buffer.concat(stdoutBuffers).toString('utf8').trim();
|
this.netCoreSdkInstalledVersion = Buffer.concat(stdoutBuffers).toString('utf8').trim();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (semver.gte(this.netCoreSdkInstalledVersion, minSupportedNetCoreVersion)) { // Net core version greater than or equal to minSupportedNetCoreVersion are supported for Build
|
// minSupportedDotnetVersion <= supported version < maxSupportedDotnetVersion
|
||||||
isSupported = true;
|
if (semver.gte(this.netCoreSdkInstalledVersion, minSupportedNetCoreVersion)) {
|
||||||
|
// If maxSupportedNetCoreVersionCutoff is not set, the latest .NET version is allowed
|
||||||
|
if (maxSupportedNetCoreVersionCutoff) {
|
||||||
|
if (semver.lt(this.netCoreSdkInstalledVersion, maxSupportedNetCoreVersionCutoff)) {
|
||||||
|
installState = netCoreInstallState.netCoreVersionSupported;
|
||||||
|
} else {
|
||||||
|
installState = netCoreInstallState.netCoreVersionTooHigh;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
installState = netCoreInstallState.netCoreVersionSupported;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
isSupported = false;
|
// .NET version is too low
|
||||||
|
installState = netCoreInstallState.netCoreVersionNotSupported;
|
||||||
}
|
}
|
||||||
resolve({ stdout: this.netCoreSdkInstalledVersion });
|
resolve({ stdout: this.netCoreSdkInstalledVersion });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -163,13 +188,8 @@ export class NetCoreTool extends ShellExecutionHelper {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isSupported) {
|
this.netCoreInstallState = installState;
|
||||||
this.netCoreInstallState = netCoreInstallState.netCoreVersionSupported;
|
return installState === netCoreInstallState.netCoreVersionSupported;
|
||||||
} else {
|
|
||||||
this.netCoreInstallState = netCoreInstallState.netCoreVersionNotSupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isSupported;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
this.netCoreInstallState = netCoreInstallState.netCoreNotPresent;
|
this.netCoreInstallState = netCoreInstallState.netCoreNotPresent;
|
||||||
@@ -185,6 +205,8 @@ export class NetCoreTool extends ShellExecutionHelper {
|
|||||||
if (!(await this.findOrInstallNetCore())) {
|
if (!(await this.findOrInstallNetCore())) {
|
||||||
if (this.netCoreInstallState === netCoreInstallState.netCoreNotPresent) {
|
if (this.netCoreInstallState === netCoreInstallState.netCoreNotPresent) {
|
||||||
throw new DotNetError(NetCoreInstallationConfirmation);
|
throw new DotNetError(NetCoreInstallationConfirmation);
|
||||||
|
} else if (this.netCoreInstallState === netCoreInstallState.netCoreVersionTooHigh && vscode.workspace.getConfiguration(DBProjectConfigurationKey)[NetCoreDowngradeDoNotShowAgainKey] === true) {
|
||||||
|
// Assume user has used global.json to override SDK version and proceed with build as is
|
||||||
} else {
|
} else {
|
||||||
throw new DotNetError(NetCoreSupportedVersionInstallationConfirmation(this.netCoreSdkInstalledVersion!));
|
throw new DotNetError(NetCoreSupportedVersionInstallationConfirmation(this.netCoreSdkInstalledVersion!));
|
||||||
}
|
}
|
||||||
|
|||||||
3
extensions/sql-migration/images/retry.svg
Normal file
3
extensions/sql-migration/images/retry.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M13 1.00029H14V5.00027H10V4.00027H12.1953C11.9297 3.54194 11.612 3.12788 11.2422 2.75809C10.8724 2.3883 10.4609 2.0732 10.0078 1.81278C9.55469 1.55237 9.07552 1.35185 8.57031 1.21122C8.0651 1.0706 7.54167 1.00029 7 1.00029C6.28125 1.00029 5.59635 1.12008 4.94531 1.35966C4.29427 1.59924 3.70833 1.93518 3.1875 2.36747C2.66667 2.79976 2.22396 3.31278 1.85938 3.90652C1.49479 4.50027 1.24479 5.14871 1.10938 5.85183L0.125 5.65652C0.229167 5.10964 0.393229 4.59142 0.617188 4.10183C0.841146 3.61225 1.11719 3.15913 1.44531 2.74247C1.77344 2.3258 2.14062 1.94559 2.54688 1.60184C2.95312 1.2581 3.39583 0.971639 3.875 0.742474C4.35417 0.513308 4.85417 0.331017 5.375 0.195601C5.89583 0.0601849 6.4375 -0.00491896 7 0.000289351C7.61458 0.000289351 8.21354 0.078414 8.79688 0.234663C9.38021 0.390913 9.92969 0.617474 10.4453 0.914348C10.9609 1.21122 11.4375 1.56799 11.875 1.98466C12.3125 2.40132 12.6875 2.87007 13 3.3909V1.00029ZM7 13.0002C7.71875 13.0002 8.40365 12.8804 9.05469 12.6409C9.70573 12.4013 10.2917 12.0653 10.8125 11.6331C11.3333 11.2008 11.776 10.6903 12.1406 10.1018C12.5052 9.51327 12.7552 8.86483 12.8906 8.1565L13.8672 8.344C13.7109 9.16692 13.4219 9.92212 13 10.6096C12.5781 11.2971 12.0625 11.8935 11.4531 12.3987C10.8438 12.9039 10.1589 13.2971 9.39844 13.5784C8.63802 13.8596 7.83854 14.0002 7 14.0002C6.38021 14.0002 5.77865 13.9221 5.19531 13.7659C4.61198 13.6096 4.0599 13.383 3.53906 13.0862C3.01823 12.7893 2.54427 12.4351 2.11719 12.0237C1.6901 11.6122 1.31771 11.1409 1 10.6096V13.0002H0V9.00025H4V10.0002H1.80469C2.07031 10.4638 2.38802 10.8805 2.75781 11.2502C3.1276 11.62 3.53906 11.9325 3.99219 12.1877C4.44531 12.4429 4.92188 12.6435 5.42188 12.7893C5.92188 12.9351 6.44792 13.0054 7 13.0002Z" fill="#0078D4"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -2,7 +2,7 @@
|
|||||||
"name": "sql-migration",
|
"name": "sql-migration",
|
||||||
"displayName": "%displayName%",
|
"displayName": "%displayName%",
|
||||||
"description": "%description%",
|
"description": "%description%",
|
||||||
"version": "0.1.8",
|
"version": "0.1.9",
|
||||||
"publisher": "Microsoft",
|
"publisher": "Microsoft",
|
||||||
"preview": true,
|
"preview": true,
|
||||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
"aiKey": "AIF-37eefaf0-8022-4671-a3fb-64752724682e",
|
"aiKey": "AIF-37eefaf0-8022-4671-a3fb-64752724682e",
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "*",
|
"vscode": "*",
|
||||||
"azdata": ">=1.29.0"
|
"azdata": ">=1.33.0"
|
||||||
},
|
},
|
||||||
"activationEvents": [
|
"activationEvents": [
|
||||||
"onDashboardOpen",
|
"onDashboardOpen",
|
||||||
@@ -81,6 +81,11 @@
|
|||||||
"command": "sqlmigration.cancel.migration",
|
"command": "sqlmigration.cancel.migration",
|
||||||
"title": "%cancel-migration-menu%",
|
"title": "%cancel-migration-menu%",
|
||||||
"category": "%migration-context-menu-category%"
|
"category": "%migration-context-menu-category%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlmigration.retry.migration",
|
||||||
|
"title": "%retry-migration-menu%",
|
||||||
|
"category": "%migration-context-menu-category%"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"menu": {
|
"menu": {
|
||||||
@@ -116,6 +121,10 @@
|
|||||||
{
|
{
|
||||||
"command": "sqlmigration.cancel.migration",
|
"command": "sqlmigration.cancel.migration",
|
||||||
"when": "false"
|
"when": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "sqlmigration.retry.migration",
|
||||||
|
"when": "false"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -174,4 +183,4 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/uuid": "^8.3.1"
|
"@types/uuid": "^8.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,5 +14,6 @@
|
|||||||
"view-target-menu": "Azure SQL Target details",
|
"view-target-menu": "Azure SQL Target details",
|
||||||
"view-service-menu": "Database Migration Service details",
|
"view-service-menu": "Database Migration Service details",
|
||||||
"copy-migration-menu": "Copy migration details",
|
"copy-migration-menu": "Copy migration details",
|
||||||
"cancel-migration-menu": "Cancel migration"
|
"cancel-migration-menu": "Cancel migration",
|
||||||
|
"retry-migration-menu": "Retry migration"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ export async function getLocations(account: azdata.Account, subscription: Subscr
|
|||||||
if (response.errors.length > 0) {
|
if (response.errors.length > 0) {
|
||||||
throw new Error(response.errors.toString());
|
throw new Error(response.errors.toString());
|
||||||
}
|
}
|
||||||
sortResourceArrayByName(response.locations);
|
|
||||||
|
|
||||||
const filteredLocations = response.locations.filter(loc => {
|
const filteredLocations = response.locations
|
||||||
return sqlMigrationResourceLocations.includes(loc.displayName);
|
.filter(loc => sqlMigrationResourceLocations.includes(loc.displayName));
|
||||||
});
|
|
||||||
|
sortResourceArrayByName(filteredLocations);
|
||||||
|
|
||||||
return filteredLocations;
|
return filteredLocations;
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ export type AzureProduct = azureResource.AzureGraphResource;
|
|||||||
|
|
||||||
export async function getResourceGroups(account: azdata.Account, subscription: Subscription): Promise<azureResource.AzureResourceResourceGroup[]> {
|
export async function getResourceGroups(account: azdata.Account, subscription: Subscription): Promise<azureResource.AzureResourceResourceGroup[]> {
|
||||||
const api = await getAzureCoreAPI();
|
const api = await getAzureCoreAPI();
|
||||||
const result = await api.getResourceGroups(account, subscription, false);
|
const result = await api.getResourceGroups(account, subscription, true);
|
||||||
sortResourceArrayByName(result.resourceGroups);
|
sortResourceArrayByName(result.resourceGroups);
|
||||||
return result.resourceGroups;
|
return result.resourceGroups;
|
||||||
}
|
}
|
||||||
@@ -362,6 +362,15 @@ export function getFullResourceGroupFromId(id: string): string {
|
|||||||
return id.replace(RegExp('/providers/.*'), '').toLowerCase();
|
return id.replace(RegExp('/providers/.*'), '').toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getResourceName(id: string): string {
|
||||||
|
const splitResourceId = id.split('/');
|
||||||
|
return splitResourceId[splitResourceId.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBlobContainerId(resourceGroupId: string, storageAccountName: string, blobContainerName: string): string {
|
||||||
|
return `${resourceGroupId}/providers/Microsoft.Storage/storageAccounts/${storageAccountName}/blobServices/default/containers/${blobContainerName}`;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SqlMigrationServiceProperties {
|
export interface SqlMigrationServiceProperties {
|
||||||
name: string;
|
name: string;
|
||||||
subscriptionId: string;
|
subscriptionId: string;
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { MigrationContext } from '../models/migrationLocalStorage';
|
import { MigrationContext, MigrationStatus } from '../models/migrationLocalStorage';
|
||||||
|
import { MigrationMode, MigrationTargetType } from '../models/stateMachine';
|
||||||
import * as loc from './strings';
|
import * as loc from './strings';
|
||||||
|
|
||||||
export enum SQLTargetAssetType {
|
export enum SQLTargetAssetType {
|
||||||
@@ -22,6 +23,28 @@ export function getMigrationTargetType(migration: MigrationContext): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMigrationMode(migration: MigrationContext): string {
|
export function getMigrationTargetTypeEnum(migration: MigrationContext): MigrationTargetType | undefined {
|
||||||
return migration.migrationContext.properties.offlineConfiguration?.offline?.valueOf() ? loc.OFFLINE : loc.OFFLINE;
|
switch (migration.targetManagedInstance.type) {
|
||||||
|
case SQLTargetAssetType.SQLMI:
|
||||||
|
return MigrationTargetType.SQLMI;
|
||||||
|
case SQLTargetAssetType.SQLVM:
|
||||||
|
return MigrationTargetType.SQLVM;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMigrationMode(migration: MigrationContext): string {
|
||||||
|
return migration.migrationContext.properties.offlineConfiguration?.offline?.valueOf() ? loc.OFFLINE : loc.ONLINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMigrationModeEnum(migration: MigrationContext): MigrationMode {
|
||||||
|
return migration.migrationContext.properties.offlineConfiguration?.offline?.valueOf() ? MigrationMode.OFFLINE : MigrationMode.ONLINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canRetryMigration(status: string | undefined): boolean {
|
||||||
|
return status === undefined ||
|
||||||
|
status === MigrationStatus.Failed ||
|
||||||
|
status === MigrationStatus.Succeeded ||
|
||||||
|
status === MigrationStatus.Canceled;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export class IconPathHelper {
|
|||||||
public static newSupportRequest: IconPath;
|
public static newSupportRequest: IconPath;
|
||||||
public static emptyTable: IconPath;
|
public static emptyTable: IconPath;
|
||||||
public static addAzureAccount: IconPath;
|
public static addAzureAccount: IconPath;
|
||||||
|
public static retry: IconPath;
|
||||||
|
|
||||||
public static setExtensionContext(context: vscode.ExtensionContext) {
|
public static setExtensionContext(context: vscode.ExtensionContext) {
|
||||||
IconPathHelper.copy = {
|
IconPathHelper.copy = {
|
||||||
@@ -153,5 +154,9 @@ export class IconPathHelper {
|
|||||||
light: context.asAbsolutePath('images/noAzureAccount.svg'),
|
light: context.asAbsolutePath('images/noAzureAccount.svg'),
|
||||||
dark: context.asAbsolutePath('images/noAzureAccount.svg')
|
dark: context.asAbsolutePath('images/noAzureAccount.svg')
|
||||||
};
|
};
|
||||||
|
IconPathHelper.retry = {
|
||||||
|
light: context.asAbsolutePath('images/retry.svg'),
|
||||||
|
dark: context.asAbsolutePath('images/retry.svg')
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,10 +92,19 @@ export function accountLinkedMessage(count: number): string {
|
|||||||
}
|
}
|
||||||
export const AZURE_TENANT = localize('sql.migration.azure.tenant', "Azure AD tenant");
|
export const AZURE_TENANT = localize('sql.migration.azure.tenant', "Azure AD tenant");
|
||||||
export function ACCOUNT_STALE_ERROR(account: AzureAccount) {
|
export function ACCOUNT_STALE_ERROR(account: AzureAccount) {
|
||||||
return localize('azure.accounts.accountStaleError', "The access token for selected account '{0}' is no longer valid. Select 'Link account' and refresh the account, or select a different account.", `${account.displayInfo.displayName} (${account.displayInfo.userId})`);
|
return localize(
|
||||||
|
'azure.accounts.accountStaleError',
|
||||||
|
"The access token for selected account '{0}' and tenant '{1}' is no longer valid. Select 'Link account' and refresh the account, or select a different account.",
|
||||||
|
`${account?.displayInfo?.displayName} (${account?.displayInfo?.userId})`,
|
||||||
|
`${account?.properties?.tenants[0]?.displayName} (${account?.properties?.tenants[0]?.userId})`);
|
||||||
}
|
}
|
||||||
export function ACCOUNT_ACCESS_ERROR(account: AzureAccount, error: Error) {
|
export function ACCOUNT_ACCESS_ERROR(account: AzureAccount, error: Error) {
|
||||||
return localize('azure.accounts.accountAccessError', "An error occurred while accessing the selected account '{0}'. Select 'Link account' and refresh the account, or select a different account. Error '{1}'", `${account.displayInfo.displayName} (${account.displayInfo.userId})`, error.message);
|
return localize(
|
||||||
|
'azure.accounts.accountAccessError',
|
||||||
|
"An error occurred while accessing the selected account '{0}' and tenant '{1}'. Select 'Link account' and refresh the account, or select a different account. Error '{2}'",
|
||||||
|
`${account?.displayInfo?.displayName} (${account?.displayInfo?.userId})`,
|
||||||
|
`${account?.properties?.tenants[0]?.displayName} (${account?.properties?.tenants[0]?.userId})`,
|
||||||
|
error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
// database backup page
|
// database backup page
|
||||||
@@ -537,7 +546,14 @@ export const AUTHENTICATION_TYPE = localize('sql.migration.authentication.type',
|
|||||||
export const REFRESH_BUTTON_LABEL = localize('sql.migration.status.refresh.label', 'Refresh');
|
export const REFRESH_BUTTON_LABEL = localize('sql.migration.status.refresh.label', 'Refresh');
|
||||||
|
|
||||||
// Saved Assessment Dialog
|
// Saved Assessment Dialog
|
||||||
|
|
||||||
export const NEXT_LABEL = localize('sql.migration.saved.assessment.next', "Next");
|
export const NEXT_LABEL = localize('sql.migration.saved.assessment.next', "Next");
|
||||||
export const CANCEL_LABEL = localize('sql.migration.saved.assessment.cancel', "Cancel");
|
export const CANCEL_LABEL = localize('sql.migration.saved.assessment.cancel', "Cancel");
|
||||||
export const SAVED_ASSESSMENT_RESULT = localize('sql.migration.saved.assessment.result', "Saved assessment result");
|
export const SAVED_ASSESSMENT_RESULT = localize('sql.migration.saved.assessment.result', "Saved assessment result");
|
||||||
|
|
||||||
|
// Retry Migration
|
||||||
|
export const MIGRATION_CANNOT_RETRY = localize('sql.migration.cannot.retry', 'Migration cannot be retried.');
|
||||||
|
export const RETRY_MIGRATION = localize('sql.migration.retry.migration', "Retry migration");
|
||||||
|
export const MIGRATION_RETRY_ERROR = localize('sql.migration.retry.migration.error', 'An error occurred while retrying the migration.');
|
||||||
|
|
||||||
|
export const INVALID_OWNER_URI = localize('sql.migration.invalid.owner.uri.error', 'Cannot connect to the database due to invalid OwnerUri (Parameter \'OwnerUri\')');
|
||||||
|
export const DATABASE_BACKUP_PAGE_LOAD_ERROR = localize('sql.migration.database.backup.load.error', 'An error occurred while accessing database details.');
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ interface StatusCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class DashboardWidget {
|
export class DashboardWidget {
|
||||||
|
private _context: vscode.ExtensionContext;
|
||||||
|
|
||||||
private _migrationStatusCardsContainer!: azdata.FlexContainer;
|
private _migrationStatusCardsContainer!: azdata.FlexContainer;
|
||||||
private _migrationStatusCardLoadingContainer!: azdata.LoadingComponent;
|
private _migrationStatusCardLoadingContainer!: azdata.LoadingComponent;
|
||||||
@@ -52,7 +53,8 @@ export class DashboardWidget {
|
|||||||
|
|
||||||
private isRefreshing: boolean = false;
|
private isRefreshing: boolean = false;
|
||||||
|
|
||||||
constructor() {
|
constructor(context: vscode.ExtensionContext) {
|
||||||
|
this._context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getCurrentMigrations(): Promise<MigrationContext[]> {
|
private async getCurrentMigrations(): Promise<MigrationContext[]> {
|
||||||
@@ -470,7 +472,7 @@ export class DashboardWidget {
|
|||||||
|
|
||||||
this._disposables.push(this._viewAllMigrationsButton.onDidClick(async (e) => {
|
this._disposables.push(this._viewAllMigrationsButton.onDidClick(async (e) => {
|
||||||
const migrationStatus = await this.getCurrentMigrations();
|
const migrationStatus = await this.getCurrentMigrations();
|
||||||
new MigrationStatusDialog(migrationStatus ? migrationStatus : await this.getMigrations(), AdsMigrationStatus.ALL).initialize();
|
new MigrationStatusDialog(this._context, migrationStatus ? migrationStatus : await this.getMigrations(), AdsMigrationStatus.ALL).initialize();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const refreshButton = view.modelBuilder.hyperlink().withProps({
|
const refreshButton = view.modelBuilder.hyperlink().withProps({
|
||||||
@@ -596,7 +598,7 @@ export class DashboardWidget {
|
|||||||
loc.MIGRATION_IN_PROGRESS
|
loc.MIGRATION_IN_PROGRESS
|
||||||
);
|
);
|
||||||
this._disposables.push(this._inProgressMigrationButton.container.onDidClick(async (e) => {
|
this._disposables.push(this._inProgressMigrationButton.container.onDidClick(async (e) => {
|
||||||
const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.ONGOING);
|
const dialog = new MigrationStatusDialog(this._context, await this.getCurrentMigrations(), AdsMigrationStatus.ONGOING);
|
||||||
dialog.initialize();
|
dialog.initialize();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -610,7 +612,7 @@ export class DashboardWidget {
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
this._disposables.push(this._inProgressWarningMigrationButton.container.onDidClick(async (e) => {
|
this._disposables.push(this._inProgressWarningMigrationButton.container.onDidClick(async (e) => {
|
||||||
const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.ONGOING);
|
const dialog = new MigrationStatusDialog(this._context, await this.getCurrentMigrations(), AdsMigrationStatus.ONGOING);
|
||||||
dialog.initialize();
|
dialog.initialize();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -623,7 +625,7 @@ export class DashboardWidget {
|
|||||||
loc.MIGRATION_COMPLETED
|
loc.MIGRATION_COMPLETED
|
||||||
);
|
);
|
||||||
this._disposables.push(this._successfulMigrationButton.container.onDidClick(async (e) => {
|
this._disposables.push(this._successfulMigrationButton.container.onDidClick(async (e) => {
|
||||||
const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.SUCCEEDED);
|
const dialog = new MigrationStatusDialog(this._context, await this.getCurrentMigrations(), AdsMigrationStatus.SUCCEEDED);
|
||||||
dialog.initialize();
|
dialog.initialize();
|
||||||
}));
|
}));
|
||||||
this._migrationStatusCardsContainer.addItem(
|
this._migrationStatusCardsContainer.addItem(
|
||||||
@@ -636,7 +638,7 @@ export class DashboardWidget {
|
|||||||
loc.MIGRATION_CUTOVER_CARD
|
loc.MIGRATION_CUTOVER_CARD
|
||||||
);
|
);
|
||||||
this._disposables.push(this._completingMigrationButton.container.onDidClick(async (e) => {
|
this._disposables.push(this._completingMigrationButton.container.onDidClick(async (e) => {
|
||||||
const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.COMPLETING);
|
const dialog = new MigrationStatusDialog(this._context, await this.getCurrentMigrations(), AdsMigrationStatus.COMPLETING);
|
||||||
dialog.initialize();
|
dialog.initialize();
|
||||||
}));
|
}));
|
||||||
this._migrationStatusCardsContainer.addItem(
|
this._migrationStatusCardsContainer.addItem(
|
||||||
@@ -648,7 +650,7 @@ export class DashboardWidget {
|
|||||||
loc.MIGRATION_FAILED
|
loc.MIGRATION_FAILED
|
||||||
);
|
);
|
||||||
this._disposables.push(this._failedMigrationButton.container.onDidClick(async (e) => {
|
this._disposables.push(this._failedMigrationButton.container.onDidClick(async (e) => {
|
||||||
const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.FAILED);
|
const dialog = new MigrationStatusDialog(this._context, await this.getCurrentMigrations(), AdsMigrationStatus.FAILED);
|
||||||
dialog.initialize();
|
dialog.initialize();
|
||||||
}));
|
}));
|
||||||
this._migrationStatusCardsContainer.addItem(
|
this._migrationStatusCardsContainer.addItem(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { MigrationStateModel, MigrationTargetType } from '../../models/stateMachine';
|
import { MigrationStateModel, MigrationTargetType, Page } from '../../models/stateMachine';
|
||||||
import { SqlDatabaseTree } from './sqlDatabasesTree';
|
import { SqlDatabaseTree } from './sqlDatabasesTree';
|
||||||
import { SqlMigrationImpactedObjectInfo } from '../../../../mssql/src/mssql';
|
import { SqlMigrationImpactedObjectInfo } from '../../../../mssql/src/mssql';
|
||||||
import { SKURecommendationPage } from '../../wizard/skuRecommendationPage';
|
import { SKURecommendationPage } from '../../wizard/skuRecommendationPage';
|
||||||
@@ -32,7 +32,7 @@ export class AssessmentResultsDialog {
|
|||||||
|
|
||||||
constructor(public ownerUri: string, public model: MigrationStateModel, public title: string, private _skuRecommendationPage: SKURecommendationPage, private _targetType: MigrationTargetType) {
|
constructor(public ownerUri: string, public model: MigrationStateModel, public title: string, private _skuRecommendationPage: SKURecommendationPage, private _targetType: MigrationTargetType) {
|
||||||
this._model = model;
|
this._model = model;
|
||||||
if (this._model.resumeAssessment && this._model.savedInfo.closedPage >= 2) {
|
if (this._model.resumeAssessment && this._model.savedInfo.closedPage >= Page.DatabaseBackup) {
|
||||||
this._model._databaseAssessment = <string[]>this._model.savedInfo.databaseAssessment;
|
this._model._databaseAssessment = <string[]>this._model.savedInfo.databaseAssessment;
|
||||||
}
|
}
|
||||||
this._tree = new SqlDatabaseTree(this._model, this._targetType);
|
this._tree = new SqlDatabaseTree(this._model, this._targetType);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { SqlMigrationAssessmentResultItem, SqlMigrationImpactedObjectInfo } from '../../../../mssql/src/mssql';
|
import { SqlMigrationAssessmentResultItem, SqlMigrationImpactedObjectInfo } from '../../../../mssql/src/mssql';
|
||||||
import { MigrationStateModel, MigrationTargetType } from '../../models/stateMachine';
|
import { MigrationStateModel, MigrationTargetType, Page } from '../../models/stateMachine';
|
||||||
import * as constants from '../../constants/strings';
|
import * as constants from '../../constants/strings';
|
||||||
import { debounce } from '../../api/utils';
|
import { debounce } from '../../api/utils';
|
||||||
import { IconPath, IconPathHelper } from '../../constants/iconPathHelper';
|
import { IconPath, IconPathHelper } from '../../constants/iconPathHelper';
|
||||||
@@ -142,7 +142,7 @@ export class SqlDatabaseTree {
|
|||||||
...styles.BOLD_NOTE_CSS,
|
...styles.BOLD_NOTE_CSS,
|
||||||
'margin': '0px 15px 0px 15px'
|
'margin': '0px 15px 0px 15px'
|
||||||
},
|
},
|
||||||
value: constants.DATABASES(0, this._model._databaseAssessment.length)
|
value: constants.DATABASES(0, this._model._databaseAssessment?.length)
|
||||||
}).component();
|
}).component();
|
||||||
return this._databaseCount;
|
return this._databaseCount;
|
||||||
}
|
}
|
||||||
@@ -187,10 +187,7 @@ export class SqlDatabaseTree {
|
|||||||
).component();
|
).component();
|
||||||
|
|
||||||
this._disposables.push(this._databaseTable.onDataChanged(async () => {
|
this._disposables.push(this._databaseTable.onDataChanged(async () => {
|
||||||
await this._databaseCount.updateProperties({
|
await this.updateValuesOnSelection();
|
||||||
'value': constants.DATABASES(this.selectedDbs().length, this._model._databaseAssessment.length)
|
|
||||||
});
|
|
||||||
this._model._databaseSelection = <azdata.DeclarativeTableCellValue[][]>this._databaseTable.dataValues;
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._disposables.push(this._databaseTable.onRowSelected(async (e) => {
|
this._disposables.push(this._databaseTable.onRowSelected(async (e) => {
|
||||||
@@ -200,7 +197,7 @@ export class SqlDatabaseTree {
|
|||||||
this._activeIssues = [];
|
this._activeIssues = [];
|
||||||
}
|
}
|
||||||
this._dbName.value = this._dbNames[e.row];
|
this._dbName.value = this._dbNames[e.row];
|
||||||
this._recommendationTitle.value = constants.ISSUES_COUNT(this._activeIssues.length);
|
this._recommendationTitle.value = constants.ISSUES_COUNT(this._activeIssues?.length);
|
||||||
this._recommendation.value = constants.ISSUES_DETAILS;
|
this._recommendation.value = constants.ISSUES_DETAILS;
|
||||||
await this._resultComponent.updateCssStyles({
|
await this._resultComponent.updateCssStyles({
|
||||||
'display': 'block'
|
'display': 'block'
|
||||||
@@ -307,7 +304,7 @@ export class SqlDatabaseTree {
|
|||||||
'display': 'none'
|
'display': 'none'
|
||||||
});
|
});
|
||||||
this._recommendation.value = constants.WARNINGS_DETAILS;
|
this._recommendation.value = constants.WARNINGS_DETAILS;
|
||||||
this._recommendationTitle.value = constants.WARNINGS_COUNT(this._activeIssues.length);
|
this._recommendationTitle.value = constants.WARNINGS_COUNT(this._activeIssues?.length);
|
||||||
if (this._targetType === MigrationTargetType.SQLMI) {
|
if (this._targetType === MigrationTargetType.SQLMI) {
|
||||||
await this.refreshResults();
|
await this.refreshResults();
|
||||||
}
|
}
|
||||||
@@ -424,7 +421,7 @@ export class SqlDatabaseTree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private handleFailedAssessment(): boolean {
|
private handleFailedAssessment(): boolean {
|
||||||
const failedAssessment: boolean = this._model._assessmentResults.assessmentError !== undefined
|
const failedAssessment: boolean = this._model._assessmentResults?.assessmentError !== undefined
|
||||||
|| (this._model._assessmentResults?.errors?.length || 0) > 0;
|
|| (this._model._assessmentResults?.errors?.length || 0) > 0;
|
||||||
if (failedAssessment) {
|
if (failedAssessment) {
|
||||||
this._dialog.message = {
|
this._dialog.message = {
|
||||||
@@ -439,12 +436,12 @@ export class SqlDatabaseTree {
|
|||||||
|
|
||||||
private getAssessmentError(): string {
|
private getAssessmentError(): string {
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
const assessmentError = this._model._assessmentResults.assessmentError;
|
const assessmentError = this._model._assessmentResults?.assessmentError;
|
||||||
if (assessmentError) {
|
if (assessmentError) {
|
||||||
errors.push(`message: ${assessmentError.message}${EOL}stack: ${assessmentError.stack}`);
|
errors.push(`message: ${assessmentError.message}${EOL}stack: ${assessmentError.stack}`);
|
||||||
}
|
}
|
||||||
if (this._model?._assessmentResults?.errors?.length! > 0) {
|
if (this._model?._assessmentResults?.errors?.length! > 0) {
|
||||||
errors.push(...this._model._assessmentResults.errors?.map(
|
errors.push(...this._model._assessmentResults?.errors?.map(
|
||||||
e => `message: ${e.message}${EOL}errorSummary: ${e.errorSummary}${EOL}possibleCauses: ${e.possibleCauses}${EOL}guidance: ${e.guidance}${EOL}errorId: ${e.errorId}`)!);
|
e => `message: ${e.message}${EOL}errorSummary: ${e.errorSummary}${EOL}possibleCauses: ${e.possibleCauses}${EOL}guidance: ${e.guidance}${EOL}errorId: ${e.errorId}`)!);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -791,7 +788,7 @@ export class SqlDatabaseTree {
|
|||||||
|
|
||||||
public async refreshResults(): Promise<void> {
|
public async refreshResults(): Promise<void> {
|
||||||
if (this._targetType === MigrationTargetType.SQLMI) {
|
if (this._targetType === MigrationTargetType.SQLMI) {
|
||||||
if (this._activeIssues.length === 0) {
|
if (this._activeIssues?.length === 0) {
|
||||||
/// show no issues here
|
/// show no issues here
|
||||||
await this._assessmentsTable.updateCssStyles({
|
await this._assessmentsTable.updateCssStyles({
|
||||||
'display': 'none',
|
'display': 'none',
|
||||||
@@ -858,7 +855,7 @@ export class SqlDatabaseTree {
|
|||||||
|| [];
|
|| [];
|
||||||
|
|
||||||
await this._assessmentResultsTable.setDataValues(assessmentResults);
|
await this._assessmentResultsTable.setDataValues(assessmentResults);
|
||||||
this._assessmentResultsTable.selectedRow = assessmentResults.length > 0 ? 0 : -1;
|
this._assessmentResultsTable.selectedRow = assessmentResults?.length > 0 ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async refreshAssessmentDetails(selectedIssue?: SqlMigrationAssessmentResultItem): Promise<void> {
|
public async refreshAssessmentDetails(selectedIssue?: SqlMigrationAssessmentResultItem): Promise<void> {
|
||||||
@@ -872,7 +869,7 @@ export class SqlDatabaseTree {
|
|||||||
await this._impactedObjectsTable.setDataValues(this._impactedObjects.map(
|
await this._impactedObjectsTable.setDataValues(this._impactedObjects.map(
|
||||||
(object) => [{ value: object.objectType }, { value: object.name }]));
|
(object) => [{ value: object.objectType }, { value: object.name }]));
|
||||||
|
|
||||||
this._impactedObjectsTable.selectedRow = this._impactedObjects.length > 0 ? 0 : -1;
|
this._impactedObjectsTable.selectedRow = this._impactedObjects?.length > 0 ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public refreshImpactedObject(impactedObject?: SqlMigrationImpactedObjectInfo): void {
|
public refreshImpactedObject(impactedObject?: SqlMigrationImpactedObjectInfo): void {
|
||||||
@@ -927,17 +924,17 @@ export class SqlDatabaseTree {
|
|||||||
style: styleLeft
|
style: styleLeft
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: this._model._assessmentResults.issues.length,
|
value: this._model._assessmentResults?.issues?.length,
|
||||||
style: styleRight
|
style: styleRight
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
this._model._assessmentResults.databaseAssessments.sort((db1, db2) => {
|
this._model._assessmentResults?.databaseAssessments.sort((db1, db2) => {
|
||||||
return db2.issues.length - db1.issues.length;
|
return db2.issues?.length - db1.issues?.length;
|
||||||
});
|
});
|
||||||
// Reset the dbName list so that it is in sync with the table
|
// Reset the dbName list so that it is in sync with the table
|
||||||
this._dbNames = this._model._assessmentResults.databaseAssessments.map(da => da.name);
|
this._dbNames = this._model._assessmentResults?.databaseAssessments.map(da => da.name);
|
||||||
this._model._assessmentResults.databaseAssessments.forEach((db) => {
|
this._model._assessmentResults?.databaseAssessments.forEach((db) => {
|
||||||
let selectable = true;
|
let selectable = true;
|
||||||
if (db.issues.find(item => item.databaseRestoreFails)) {
|
if (db.issues.find(item => item.databaseRestoreFails)) {
|
||||||
selectable = false;
|
selectable = false;
|
||||||
@@ -954,7 +951,7 @@ export class SqlDatabaseTree {
|
|||||||
style: styleLeft
|
style: styleLeft
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: db.issues.length,
|
value: db.issues?.length,
|
||||||
style: styleRight
|
style: styleRight
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -962,13 +959,27 @@ export class SqlDatabaseTree {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
await this._instanceTable.setDataValues(instanceTableValues);
|
await this._instanceTable.setDataValues(instanceTableValues);
|
||||||
if (this._model.resumeAssessment && this._model.savedInfo.closedPage >= 2) {
|
if (this._model.resumeAssessment && this._model.savedInfo.closedPage >= Page.SKURecommendation && this._targetType === this._model.savedInfo.migrationTargetType) {
|
||||||
await this._databaseTable.setDataValues(this._model.savedInfo.migrationDatabases);
|
await this._databaseTable.setDataValues(this._model.savedInfo.migrationDatabases);
|
||||||
} else {
|
} else {
|
||||||
|
if (this._model.retryMigration && this._targetType === this._model.savedInfo.migrationTargetType) {
|
||||||
|
const sourceDatabaseName = this._model.savedInfo.databaseList[0];
|
||||||
|
const sourceDatabaseIndex = this._dbNames.indexOf(sourceDatabaseName);
|
||||||
|
this._databaseTableValues[sourceDatabaseIndex][0].value = true;
|
||||||
|
}
|
||||||
|
|
||||||
await this._databaseTable.setDataValues(this._databaseTableValues);
|
await this._databaseTable.setDataValues(this._databaseTableValues);
|
||||||
|
await this.updateValuesOnSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async updateValuesOnSelection() {
|
||||||
|
await this._databaseCount.updateProperties({
|
||||||
|
'value': constants.DATABASES(this.selectedDbs()?.length, this._model._databaseAssessment?.length)
|
||||||
|
});
|
||||||
|
this._model._databaseSelection = <azdata.DeclarativeTableCellValue[][]>this._databaseTable.dataValues;
|
||||||
|
}
|
||||||
|
|
||||||
// undo when bug #16445 is fixed
|
// undo when bug #16445 is fixed
|
||||||
private createIconTextCell(icon: IconPath, text: string): string {
|
private createIconTextCell(icon: IconPath, text: string): string {
|
||||||
return text;
|
return text;
|
||||||
|
|||||||
@@ -12,15 +12,19 @@ import * as loc from '../../constants/strings';
|
|||||||
import { convertByteSizeToReadableUnit, convertIsoTimeToLocalTime, getSqlServerName, getMigrationStatusImage, SupportedAutoRefreshIntervals, clearDialogMessage, displayDialogErrorMessage } from '../../api/utils';
|
import { convertByteSizeToReadableUnit, convertIsoTimeToLocalTime, getSqlServerName, getMigrationStatusImage, SupportedAutoRefreshIntervals, clearDialogMessage, displayDialogErrorMessage } from '../../api/utils';
|
||||||
import { EOL } from 'os';
|
import { EOL } from 'os';
|
||||||
import { ConfirmCutoverDialog } from './confirmCutoverDialog';
|
import { ConfirmCutoverDialog } from './confirmCutoverDialog';
|
||||||
|
import { RetryMigrationDialog } from '../retryMigration/retryMigrationDialog';
|
||||||
import * as styles from '../../constants/styles';
|
import * as styles from '../../constants/styles';
|
||||||
|
import { canRetryMigration } from '../../constants/helper';
|
||||||
|
|
||||||
const refreshFrequency: SupportedAutoRefreshIntervals = 30000;
|
const refreshFrequency: SupportedAutoRefreshIntervals = 30000;
|
||||||
const statusImageSize: number = 14;
|
const statusImageSize: number = 14;
|
||||||
|
|
||||||
export class MigrationCutoverDialog {
|
export class MigrationCutoverDialog {
|
||||||
|
private _context: vscode.ExtensionContext;
|
||||||
private _dialogObject!: azdata.window.Dialog;
|
private _dialogObject!: azdata.window.Dialog;
|
||||||
private _view!: azdata.ModelView;
|
private _view!: azdata.ModelView;
|
||||||
private _model: MigrationCutoverDialogModel;
|
private _model: MigrationCutoverDialogModel;
|
||||||
|
private _migration: MigrationContext;
|
||||||
|
|
||||||
private _databaseTitleName!: azdata.TextComponent;
|
private _databaseTitleName!: azdata.TextComponent;
|
||||||
private _cutoverButton!: azdata.ButtonComponent;
|
private _cutoverButton!: azdata.ButtonComponent;
|
||||||
@@ -29,6 +33,7 @@ export class MigrationCutoverDialog {
|
|||||||
private _refreshLoader!: azdata.LoadingComponent;
|
private _refreshLoader!: azdata.LoadingComponent;
|
||||||
private _copyDatabaseMigrationDetails!: azdata.ButtonComponent;
|
private _copyDatabaseMigrationDetails!: azdata.ButtonComponent;
|
||||||
private _newSupportRequest!: azdata.ButtonComponent;
|
private _newSupportRequest!: azdata.ButtonComponent;
|
||||||
|
private _retryButton!: azdata.ButtonComponent;
|
||||||
|
|
||||||
private _sourceDatabaseInfoField!: InfoFieldSchema;
|
private _sourceDatabaseInfoField!: InfoFieldSchema;
|
||||||
private _sourceDetailsInfoField!: InfoFieldSchema;
|
private _sourceDetailsInfoField!: InfoFieldSchema;
|
||||||
@@ -53,7 +58,9 @@ export class MigrationCutoverDialog {
|
|||||||
|
|
||||||
readonly _infoFieldWidth: string = '250px';
|
readonly _infoFieldWidth: string = '250px';
|
||||||
|
|
||||||
constructor(migration: MigrationContext) {
|
constructor(context: vscode.ExtensionContext, migration: MigrationContext) {
|
||||||
|
this._context = context;
|
||||||
|
this._migration = migration;
|
||||||
this._model = new MigrationCutoverDialogModel(migration);
|
this._model = new MigrationCutoverDialogModel(migration);
|
||||||
this._dialogObject = azdata.window.createModelViewDialog('', 'MigrationCutoverDialog', 'wide');
|
this._dialogObject = azdata.window.createModelViewDialog('', 'MigrationCutoverDialog', 'wide');
|
||||||
}
|
}
|
||||||
@@ -301,11 +308,11 @@ export class MigrationCutoverDialog {
|
|||||||
iconWidth: '16px',
|
iconWidth: '16px',
|
||||||
label: loc.COMPLETE_CUTOVER,
|
label: loc.COMPLETE_CUTOVER,
|
||||||
height: '20px',
|
height: '20px',
|
||||||
width: '150px',
|
width: '140px',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
...styles.BODY_CSS,
|
...styles.BODY_CSS,
|
||||||
'display': this._isOnlineMigration() ? 'inline' : 'none'
|
'display': this._isOnlineMigration() ? 'block' : 'none'
|
||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
@@ -330,7 +337,7 @@ export class MigrationCutoverDialog {
|
|||||||
iconWidth: '16px',
|
iconWidth: '16px',
|
||||||
label: loc.CANCEL_MIGRATION,
|
label: loc.CANCEL_MIGRATION,
|
||||||
height: '20px',
|
height: '20px',
|
||||||
width: '150px',
|
width: '140px',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
...styles.BODY_CSS,
|
...styles.BODY_CSS,
|
||||||
@@ -353,6 +360,28 @@ export class MigrationCutoverDialog {
|
|||||||
flex: '0'
|
flex: '0'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._retryButton = this._view.modelBuilder.button().withProps({
|
||||||
|
label: loc.RETRY_MIGRATION,
|
||||||
|
iconPath: IconPathHelper.retry,
|
||||||
|
enabled: false,
|
||||||
|
iconHeight: '16px',
|
||||||
|
iconWidth: '16px',
|
||||||
|
height: '20px',
|
||||||
|
width: '120px',
|
||||||
|
CSSStyles: {
|
||||||
|
...styles.BODY_CSS,
|
||||||
|
}
|
||||||
|
}).component();
|
||||||
|
this._disposables.push(this._retryButton.onDidClick(
|
||||||
|
async (e) => {
|
||||||
|
await this.refreshStatus();
|
||||||
|
let retryMigrationDialog = new RetryMigrationDialog(this._context, this._migration);
|
||||||
|
await retryMigrationDialog.openDialog();
|
||||||
|
}
|
||||||
|
));
|
||||||
|
headerActions.addItem(this._retryButton, {
|
||||||
|
flex: '0',
|
||||||
|
});
|
||||||
|
|
||||||
this._refreshButton = this._view.modelBuilder.button().withProps({
|
this._refreshButton = this._view.modelBuilder.button().withProps({
|
||||||
iconPath: IconPathHelper.refresh,
|
iconPath: IconPathHelper.refresh,
|
||||||
@@ -360,7 +389,7 @@ export class MigrationCutoverDialog {
|
|||||||
iconWidth: '16px',
|
iconWidth: '16px',
|
||||||
label: 'Refresh',
|
label: 'Refresh',
|
||||||
height: '20px',
|
height: '20px',
|
||||||
width: '100px',
|
width: '80px',
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
...styles.BODY_CSS,
|
...styles.BODY_CSS,
|
||||||
}
|
}
|
||||||
@@ -379,7 +408,7 @@ export class MigrationCutoverDialog {
|
|||||||
iconWidth: '16px',
|
iconWidth: '16px',
|
||||||
label: loc.COPY_MIGRATION_DETAILS,
|
label: loc.COPY_MIGRATION_DETAILS,
|
||||||
height: '20px',
|
height: '20px',
|
||||||
width: '200px',
|
width: '160px',
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
...styles.BODY_CSS,
|
...styles.BODY_CSS,
|
||||||
}
|
}
|
||||||
@@ -406,7 +435,7 @@ export class MigrationCutoverDialog {
|
|||||||
iconHeight: '16px',
|
iconHeight: '16px',
|
||||||
iconWidth: '16px',
|
iconWidth: '16px',
|
||||||
height: '20px',
|
height: '20px',
|
||||||
width: '180px',
|
width: '160px',
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
...styles.BODY_CSS,
|
...styles.BODY_CSS,
|
||||||
}
|
}
|
||||||
@@ -567,7 +596,7 @@ export class MigrationCutoverDialog {
|
|||||||
|
|
||||||
if (this._isOnlineMigration()) {
|
if (this._isOnlineMigration()) {
|
||||||
await this._cutoverButton.updateCssStyles({
|
await this._cutoverButton.updateCssStyles({
|
||||||
'display': 'inline'
|
'display': 'block'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -720,6 +749,9 @@ export class MigrationCutoverDialog {
|
|||||||
this._cancelButton.enabled =
|
this._cancelButton.enabled =
|
||||||
migrationStatusTextValue === MigrationStatus.Creating ||
|
migrationStatusTextValue === MigrationStatus.Creating ||
|
||||||
migrationStatusTextValue === MigrationStatus.InProgress;
|
migrationStatusTextValue === MigrationStatus.InProgress;
|
||||||
|
|
||||||
|
this._retryButton.enabled = canRetryMigration(migrationStatusTextValue);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
displayDialogErrorMessage(this._dialogObject, loc.MIGRATION_STATUS_REFRESH_ERROR, e);
|
displayDialogErrorMessage(this._dialogObject, loc.MIGRATION_STATUS_REFRESH_ERROR, e);
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import { clearDialogMessage, convertTimeDifferenceToDuration, displayDialogError
|
|||||||
import { SqlMigrationServiceDetailsDialog } from '../sqlMigrationService/sqlMigrationServiceDetailsDialog';
|
import { SqlMigrationServiceDetailsDialog } from '../sqlMigrationService/sqlMigrationServiceDetailsDialog';
|
||||||
import { ConfirmCutoverDialog } from '../migrationCutover/confirmCutoverDialog';
|
import { ConfirmCutoverDialog } from '../migrationCutover/confirmCutoverDialog';
|
||||||
import { MigrationCutoverDialogModel } from '../migrationCutover/migrationCutoverDialogModel';
|
import { MigrationCutoverDialogModel } from '../migrationCutover/migrationCutoverDialogModel';
|
||||||
import { getMigrationTargetType, getMigrationMode } from '../../constants/helper';
|
import { getMigrationTargetType, getMigrationMode, canRetryMigration } from '../../constants/helper';
|
||||||
|
import { RetryMigrationDialog } from '../retryMigration/retryMigrationDialog';
|
||||||
|
|
||||||
const refreshFrequency: SupportedAutoRefreshIntervals = 180000;
|
const refreshFrequency: SupportedAutoRefreshIntervals = 180000;
|
||||||
|
|
||||||
@@ -29,9 +30,11 @@ const MenuCommands = {
|
|||||||
ViewService: 'sqlmigration.view.service',
|
ViewService: 'sqlmigration.view.service',
|
||||||
CopyMigration: 'sqlmigration.copy.migration',
|
CopyMigration: 'sqlmigration.copy.migration',
|
||||||
CancelMigration: 'sqlmigration.cancel.migration',
|
CancelMigration: 'sqlmigration.cancel.migration',
|
||||||
|
RetryMigration: 'sqlmigration.retry.migration',
|
||||||
};
|
};
|
||||||
|
|
||||||
export class MigrationStatusDialog {
|
export class MigrationStatusDialog {
|
||||||
|
private _context: vscode.ExtensionContext;
|
||||||
private _model: MigrationStatusDialogModel;
|
private _model: MigrationStatusDialogModel;
|
||||||
private _dialogObject!: azdata.window.Dialog;
|
private _dialogObject!: azdata.window.Dialog;
|
||||||
private _view!: azdata.ModelView;
|
private _view!: azdata.ModelView;
|
||||||
@@ -45,7 +48,8 @@ export class MigrationStatusDialog {
|
|||||||
|
|
||||||
private isRefreshing = false;
|
private isRefreshing = false;
|
||||||
|
|
||||||
constructor(migrations: MigrationContext[], private _filter: AdsMigrationStatus) {
|
constructor(context: vscode.ExtensionContext, migrations: MigrationContext[], private _filter: AdsMigrationStatus) {
|
||||||
|
this._context = context;
|
||||||
this._model = new MigrationStatusDialogModel(migrations);
|
this._model = new MigrationStatusDialogModel(migrations);
|
||||||
this._dialogObject = azdata.window.createModelViewDialog(loc.MIGRATION_STATUS, 'MigrationControllerDialog', 'wide');
|
this._dialogObject = azdata.window.createModelViewDialog(loc.MIGRATION_STATUS, 'MigrationControllerDialog', 'wide');
|
||||||
}
|
}
|
||||||
@@ -221,7 +225,7 @@ export class MigrationStatusDialog {
|
|||||||
async (migrationId: string) => {
|
async (migrationId: string) => {
|
||||||
try {
|
try {
|
||||||
const migration = this._model._migrations.find(migration => migration.migrationContext.id === migrationId);
|
const migration = this._model._migrations.find(migration => migration.migrationContext.id === migrationId);
|
||||||
const dialog = new MigrationCutoverDialog(migration!);
|
const dialog = new MigrationCutoverDialog(this._context, migration!);
|
||||||
await dialog.initialize();
|
await dialog.initialize();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
@@ -302,6 +306,25 @@ export class MigrationStatusDialog {
|
|||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this._disposables.push(vscode.commands.registerCommand(
|
||||||
|
MenuCommands.RetryMigration,
|
||||||
|
async (migrationId: string) => {
|
||||||
|
try {
|
||||||
|
clearDialogMessage(this._dialogObject);
|
||||||
|
const migration = this._model._migrations.find(migration => migration.migrationContext.id === migrationId);
|
||||||
|
if (canRetryMigration(migration?.migrationContext.properties.migrationStatus)) {
|
||||||
|
let retryMigrationDialog = new RetryMigrationDialog(this._context, migration!);
|
||||||
|
await retryMigrationDialog.openDialog();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await vscode.window.showInformationMessage(loc.MIGRATION_CANNOT_RETRY);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
displayDialogErrorMessage(this._dialogObject, loc.MIGRATION_RETRY_ERROR, e);
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async populateMigrationTable(): Promise<void> {
|
private async populateMigrationTable(): Promise<void> {
|
||||||
@@ -366,7 +389,7 @@ export class MigrationStatusDialog {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this._disposables.push(databaseHyperLink.onDidClick(
|
this._disposables.push(databaseHyperLink.onDidClick(
|
||||||
async (e) => await (new MigrationCutoverDialog(migration)).initialize()));
|
async (e) => await (new MigrationCutoverDialog(this._context, migration)).initialize()));
|
||||||
|
|
||||||
return this._view.modelBuilder
|
return this._view.modelBuilder
|
||||||
.flexContainer()
|
.flexContainer()
|
||||||
@@ -416,6 +439,10 @@ export class MigrationStatusDialog {
|
|||||||
menuCommands.push(MenuCommands.CancelMigration);
|
menuCommands.push(MenuCommands.CancelMigration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (canRetryMigration(migrationStatus)) {
|
||||||
|
menuCommands.push(MenuCommands.RetryMigration);
|
||||||
|
}
|
||||||
|
|
||||||
return menuCommands;
|
return menuCommands;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 * as mssql from '../../../../mssql';
|
||||||
|
import { azureResource } from 'azureResource';
|
||||||
|
import { getLocations, getResourceGroupFromId, getBlobContainerId, getFullResourceGroupFromId, getResourceName } from '../../api/azure';
|
||||||
|
import { MigrationMode, MigrationStateModel, NetworkContainerType, SavedInfo, Page } from '../../models/stateMachine';
|
||||||
|
import { MigrationContext } from '../../models/migrationLocalStorage';
|
||||||
|
import { WizardController } from '../../wizard/wizardController';
|
||||||
|
import { getMigrationModeEnum, getMigrationTargetTypeEnum } from '../../constants/helper';
|
||||||
|
|
||||||
|
export class RetryMigrationDialog {
|
||||||
|
private _context: vscode.ExtensionContext;
|
||||||
|
private _migration: MigrationContext;
|
||||||
|
|
||||||
|
constructor(context: vscode.ExtensionContext, migration: MigrationContext) {
|
||||||
|
this._context = context;
|
||||||
|
this._migration = migration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createMigrationStateModel(migration: MigrationContext, connectionId: string, serverName: string, api: mssql.IExtension, location: azureResource.AzureLocation): MigrationStateModel {
|
||||||
|
let stateModel = new MigrationStateModel(this._context, connectionId, api.sqlMigration);
|
||||||
|
|
||||||
|
const sourceDatabaseName = migration.migrationContext.properties.sourceDatabaseName;
|
||||||
|
let savedInfo: SavedInfo;
|
||||||
|
savedInfo = {
|
||||||
|
closedPage: Page.AzureAccount,
|
||||||
|
|
||||||
|
// AzureAccount
|
||||||
|
azureAccount: migration.azureAccount,
|
||||||
|
azureTenant: migration.azureAccount.properties.tenants[0],
|
||||||
|
|
||||||
|
// DatabaseSelector
|
||||||
|
selectedDatabases: [],
|
||||||
|
|
||||||
|
// SKURecommendation
|
||||||
|
databaseAssessment: [],
|
||||||
|
databaseList: [sourceDatabaseName],
|
||||||
|
migrationDatabases: [],
|
||||||
|
serverAssessment: null,
|
||||||
|
|
||||||
|
migrationTargetType: getMigrationTargetTypeEnum(migration)!,
|
||||||
|
subscription: migration.subscription,
|
||||||
|
location: location,
|
||||||
|
resourceGroup: {
|
||||||
|
id: getFullResourceGroupFromId(migration.targetManagedInstance.id),
|
||||||
|
name: getResourceGroupFromId(migration.targetManagedInstance.id),
|
||||||
|
subscription: migration.subscription
|
||||||
|
},
|
||||||
|
targetServerInstance: migration.targetManagedInstance,
|
||||||
|
|
||||||
|
// MigrationMode
|
||||||
|
migrationMode: getMigrationModeEnum(migration),
|
||||||
|
|
||||||
|
// DatabaseBackup
|
||||||
|
targetSubscription: migration.subscription,
|
||||||
|
targetDatabaseNames: [migration.migrationContext.name],
|
||||||
|
networkContainerType: null,
|
||||||
|
networkShare: null,
|
||||||
|
blobs: [],
|
||||||
|
|
||||||
|
// Integration Runtime
|
||||||
|
migrationServiceId: migration.migrationContext.properties.migrationService,
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStorageAccountResourceGroup = (storageAccountResourceId: string) => {
|
||||||
|
return {
|
||||||
|
id: getFullResourceGroupFromId(storageAccountResourceId!),
|
||||||
|
name: getResourceGroupFromId(storageAccountResourceId!),
|
||||||
|
subscription: migration.subscription
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const getStorageAccount = (storageAccountResourceId: string) => {
|
||||||
|
const storageAccountName = getResourceName(storageAccountResourceId);
|
||||||
|
return {
|
||||||
|
type: 'microsoft.storage/storageaccounts',
|
||||||
|
id: storageAccountResourceId!,
|
||||||
|
tenantId: savedInfo.azureTenant?.id!,
|
||||||
|
subscriptionId: migration.subscription.id,
|
||||||
|
name: storageAccountName,
|
||||||
|
location: savedInfo.location!.name,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const sourceLocation = migration.migrationContext.properties.backupConfiguration.sourceLocation;
|
||||||
|
if (sourceLocation?.fileShare) {
|
||||||
|
savedInfo.networkContainerType = NetworkContainerType.NETWORK_SHARE;
|
||||||
|
const storageAccountResourceId = migration.migrationContext.properties.backupConfiguration.targetLocation?.storageAccountResourceId!;
|
||||||
|
savedInfo.networkShare = {
|
||||||
|
password: '',
|
||||||
|
networkShareLocation: sourceLocation?.fileShare?.path!,
|
||||||
|
windowsUser: sourceLocation?.fileShare?.username!,
|
||||||
|
storageAccount: getStorageAccount(storageAccountResourceId!),
|
||||||
|
resourceGroup: getStorageAccountResourceGroup(storageAccountResourceId!),
|
||||||
|
storageKey: ''
|
||||||
|
};
|
||||||
|
} else if (sourceLocation?.azureBlob) {
|
||||||
|
savedInfo.networkContainerType = NetworkContainerType.BLOB_CONTAINER;
|
||||||
|
const storageAccountResourceId = sourceLocation?.azureBlob?.storageAccountResourceId!;
|
||||||
|
savedInfo.blobs = [
|
||||||
|
{
|
||||||
|
blobContainer: {
|
||||||
|
id: getBlobContainerId(getFullResourceGroupFromId(storageAccountResourceId!), getResourceName(storageAccountResourceId!), sourceLocation?.azureBlob.blobContainerName),
|
||||||
|
name: sourceLocation?.azureBlob.blobContainerName,
|
||||||
|
subscription: migration.subscription
|
||||||
|
},
|
||||||
|
lastBackupFile: getMigrationModeEnum(migration) === MigrationMode.OFFLINE ? migration.migrationContext.properties.offlineConfiguration.lastBackupName! : undefined,
|
||||||
|
storageAccount: getStorageAccount(storageAccountResourceId!),
|
||||||
|
resourceGroup: getStorageAccountResourceGroup(storageAccountResourceId!),
|
||||||
|
storageKey: ''
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
stateModel.retryMigration = true;
|
||||||
|
stateModel.savedInfo = savedInfo;
|
||||||
|
stateModel.serverName = serverName;
|
||||||
|
return stateModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async openDialog(dialogName?: string) {
|
||||||
|
const locations = await getLocations(this._migration.azureAccount, this._migration.subscription);
|
||||||
|
let location: azureResource.AzureLocation;
|
||||||
|
locations.forEach(azureLocation => {
|
||||||
|
if (azureLocation.name === this._migration.targetManagedInstance.location) {
|
||||||
|
location = azureLocation;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let activeConnection = await azdata.connection.getCurrentConnection();
|
||||||
|
let connectionId: string = '';
|
||||||
|
let serverName: string = '';
|
||||||
|
if (!activeConnection) {
|
||||||
|
const connection = await azdata.connection.openConnectionDialog();
|
||||||
|
if (connection) {
|
||||||
|
connectionId = connection.connectionId;
|
||||||
|
serverName = connection.options.server;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
connectionId = activeConnection.connectionId;
|
||||||
|
serverName = activeConnection.serverName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const api = (await vscode.extensions.getExtension(mssql.extension.name)?.activate()) as mssql.IExtension;
|
||||||
|
const stateModel = this.createMigrationStateModel(this._migration, connectionId, serverName, api, location!);
|
||||||
|
|
||||||
|
const wizardController = new WizardController(this._context, stateModel);
|
||||||
|
await wizardController.openWizard(stateModel.sourceConnectionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -108,11 +108,7 @@ class SQLMigration {
|
|||||||
await wizardController.openWizard(connectionId);
|
await wizardController.openWizard(connectionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkSavedInfo(serverName: string): SavedInfo | undefined {
|
private checkSavedInfo(serverName: string): SavedInfo | undefined {
|
||||||
@@ -138,7 +134,7 @@ let sqlMigration: SQLMigration;
|
|||||||
export async function activate(context: vscode.ExtensionContext) {
|
export async function activate(context: vscode.ExtensionContext) {
|
||||||
sqlMigration = new SQLMigration(context);
|
sqlMigration = new SQLMigration(context);
|
||||||
await sqlMigration.registerCommands();
|
await sqlMigration.registerCommands();
|
||||||
let widget = new DashboardWidget();
|
let widget = new DashboardWidget(context);
|
||||||
widget.register();
|
widget.register();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ export enum Page {
|
|||||||
export enum WizardEntryPoint {
|
export enum WizardEntryPoint {
|
||||||
Default = 'Default',
|
Default = 'Default',
|
||||||
SaveAndClose = 'SaveAndClose',
|
SaveAndClose = 'SaveAndClose',
|
||||||
|
RetryMigration = 'RetryMigration',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DatabaseBackupModel {
|
export interface DatabaseBackupModel {
|
||||||
@@ -188,6 +189,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
public refreshDatabaseBackupPage!: boolean;
|
public refreshDatabaseBackupPage!: boolean;
|
||||||
|
|
||||||
public _databaseSelection!: azdata.DeclarativeTableCellValue[][];
|
public _databaseSelection!: azdata.DeclarativeTableCellValue[][];
|
||||||
|
public retryMigration!: boolean;
|
||||||
public resumeAssessment!: boolean;
|
public resumeAssessment!: boolean;
|
||||||
public savedInfo!: SavedInfo;
|
public savedInfo!: SavedInfo;
|
||||||
public closedPage!: number;
|
public closedPage!: number;
|
||||||
@@ -293,7 +295,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
private async generateAssessmentTelemetry(): Promise<void> {
|
private async generateAssessmentTelemetry(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
let serverIssues = this._assessmentResults.issues.map(i => {
|
let serverIssues = this._assessmentResults?.issues.map(i => {
|
||||||
return {
|
return {
|
||||||
ruleId: i.ruleId,
|
ruleId: i.ruleId,
|
||||||
count: i.impactedObjects.length
|
count: i.impactedObjects.length
|
||||||
@@ -337,10 +339,10 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
'serverErrors': JSON.stringify(serverErrors),
|
'serverErrors': JSON.stringify(serverErrors),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'issuesCount': this._assessmentResults.issues.length,
|
'issuesCount': this._assessmentResults?.issues.length,
|
||||||
'warningsCount': this._assessmentResults.databaseAssessments.reduce((count, d) => count + d.issues.length, 0),
|
'warningsCount': this._assessmentResults?.databaseAssessments.reduce((count, d) => count + d.issues.length, 0),
|
||||||
'durationInMilliseconds': endTime.getTime() - startTime.getTime(),
|
'durationInMilliseconds': endTime.getTime() - startTime.getTime(),
|
||||||
'databaseCount': this._assessmentResults.databaseAssessments.length,
|
'databaseCount': this._assessmentResults?.databaseAssessments.length,
|
||||||
'serverHostCpuCount': this._assessmentApiResponse?.assessmentResult?.cpuCoreCount,
|
'serverHostCpuCount': this._assessmentApiResponse?.assessmentResult?.cpuCoreCount,
|
||||||
'serverHostPhysicalMemoryInBytes': this._assessmentApiResponse?.assessmentResult?.physicalServerMemory,
|
'serverHostPhysicalMemoryInBytes': this._assessmentApiResponse?.assessmentResult?.physicalServerMemory,
|
||||||
'serverDatabases': this._assessmentApiResponse?.assessmentResult?.numberOfUserDatabases,
|
'serverDatabases': this._assessmentApiResponse?.assessmentResult?.numberOfUserDatabases,
|
||||||
@@ -626,12 +628,12 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
|
|
||||||
public async getManagedInstanceValues(subscription: azureResource.AzureResourceSubscription, location: azureResource.AzureLocation, resourceGroup: azureResource.AzureResourceResourceGroup): Promise<azdata.CategoryValue[]> {
|
public async getManagedInstanceValues(subscription: azureResource.AzureResourceSubscription, location: azureResource.AzureLocation, resourceGroup: azureResource.AzureResourceResourceGroup): Promise<azdata.CategoryValue[]> {
|
||||||
let managedInstanceValues: azdata.CategoryValue[] = [];
|
let managedInstanceValues: azdata.CategoryValue[] = [];
|
||||||
if (!this._azureAccount) {
|
if (!this._azureAccount || !subscription) {
|
||||||
return managedInstanceValues;
|
return managedInstanceValues;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this._targetManagedInstances = (await getAvailableManagedInstanceProducts(this._azureAccount, subscription)).filter((mi) => {
|
this._targetManagedInstances = (await getAvailableManagedInstanceProducts(this._azureAccount, subscription)).filter((mi) => {
|
||||||
if (mi.location.toLowerCase() === location.name.toLowerCase() && mi.resourceGroup?.toLowerCase() === resourceGroup?.name.toLowerCase()) {
|
if (mi.location.toLowerCase() === location?.name.toLowerCase() && mi.resourceGroup?.toLowerCase() === resourceGroup?.name.toLowerCase()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -678,7 +680,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
try {
|
try {
|
||||||
if (this._azureAccount && subscription && resourceGroup) {
|
if (this._azureAccount && subscription && resourceGroup) {
|
||||||
this._targetSqlVirtualMachines = (await getAvailableSqlVMs(this._azureAccount, subscription, resourceGroup)).filter((virtualMachine) => {
|
this._targetSqlVirtualMachines = (await getAvailableSqlVMs(this._azureAccount, subscription, resourceGroup)).filter((virtualMachine) => {
|
||||||
if (virtualMachine.location === location.name) {
|
if (virtualMachine?.location?.toLowerCase() === location?.name?.toLowerCase()) {
|
||||||
if (virtualMachine.properties.sqlImageOffer) {
|
if (virtualMachine.properties.sqlImageOffer) {
|
||||||
return virtualMachine.properties.sqlImageOffer.toLowerCase().includes('-ws'); //filtering out all non windows sql vms.
|
return virtualMachine.properties.sqlImageOffer.toLowerCase().includes('-ws'); //filtering out all non windows sql vms.
|
||||||
}
|
}
|
||||||
@@ -996,6 +998,8 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
let wizardEntryPoint = WizardEntryPoint.Default;
|
let wizardEntryPoint = WizardEntryPoint.Default;
|
||||||
if (this.resumeAssessment) {
|
if (this.resumeAssessment) {
|
||||||
wizardEntryPoint = WizardEntryPoint.SaveAndClose;
|
wizardEntryPoint = WizardEntryPoint.SaveAndClose;
|
||||||
|
} else if (this.retryMigration) {
|
||||||
|
wizardEntryPoint = WizardEntryPoint.RetryMigration;
|
||||||
}
|
}
|
||||||
if (response.status === 201 || response.status === 200) {
|
if (response.status === 201 || response.status === 200) {
|
||||||
sendSqlMigrationActionEvent(
|
sendSqlMigrationActionEvent(
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||||
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
|
import { MigrationStateModel, Page, StateChangeEvent } from '../models/stateMachine';
|
||||||
import * as constants from '../constants/strings';
|
import * as constants from '../constants/strings';
|
||||||
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
||||||
import { deepClone, findDropDownItemIndex, selectDropDownIndex } from '../api/utils';
|
import { deepClone, findDropDownItemIndex, selectDropDownIndex } from '../api/utils';
|
||||||
@@ -82,6 +82,7 @@ export class AccountsSelectionPage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
if (this.migrationStateModel._azureAccount?.isStale) {
|
if (this.migrationStateModel._azureAccount?.isStale) {
|
||||||
this.wizard.message = {
|
this.wizard.message = {
|
||||||
|
level: azdata.window.MessageLevel.Error,
|
||||||
text: constants.ACCOUNT_STALE_ERROR(this.migrationStateModel._azureAccount)
|
text: constants.ACCOUNT_STALE_ERROR(this.migrationStateModel._azureAccount)
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
@@ -111,9 +112,9 @@ export class AccountsSelectionPage extends MigrationWizardPage {
|
|||||||
await this._accountTenantFlexContainer.updateCssStyles({
|
await this._accountTenantFlexContainer.updateCssStyles({
|
||||||
'display': 'none'
|
'display': 'none'
|
||||||
});
|
});
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= 0) {
|
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.AzureAccount)) {
|
||||||
(<azdata.CategoryValue[]>this._azureAccountsDropdown.values)?.forEach((account, index) => {
|
(<azdata.CategoryValue[]>this._azureAccountsDropdown.values)?.forEach((account, index) => {
|
||||||
if (account.name === this.migrationStateModel.savedInfo.azureAccount?.displayInfo.userId) {
|
if (account.name.toLowerCase() === this.migrationStateModel.savedInfo.azureAccount?.displayInfo.userId.toLowerCase()) {
|
||||||
selectDropDownIndex(this._azureAccountsDropdown, index);
|
selectDropDownIndex(this._azureAccountsDropdown, index);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -193,8 +194,6 @@ export class AccountsSelectionPage extends MigrationWizardPage {
|
|||||||
this.migrationStateModel._targetSubscription = undefined!;
|
this.migrationStateModel._targetSubscription = undefined!;
|
||||||
this.migrationStateModel._databaseBackup.subscription = undefined!;
|
this.migrationStateModel._databaseBackup.subscription = undefined!;
|
||||||
}
|
}
|
||||||
const selectedAzureAccount = this.migrationStateModel.getAccount(selectedIndex);
|
|
||||||
this.migrationStateModel._azureAccount = deepClone(selectedAzureAccount);
|
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -233,16 +232,24 @@ export class AccountsSelectionPage extends MigrationWizardPage {
|
|||||||
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||||
this.wizard.registerNavigationValidator(async pageChangeInfo => {
|
this.wizard.registerNavigationValidator(async pageChangeInfo => {
|
||||||
try {
|
try {
|
||||||
if (!this.migrationStateModel._azureAccount?.isStale) {
|
this.wizard.message = { text: '', };
|
||||||
|
|
||||||
|
if (this.migrationStateModel._azureAccount && !this.migrationStateModel._azureAccount?.isStale) {
|
||||||
const subscriptions = await getSubscriptions(this.migrationStateModel._azureAccount);
|
const subscriptions = await getSubscriptions(this.migrationStateModel._azureAccount);
|
||||||
if (subscriptions?.length > 0) {
|
if (subscriptions?.length > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.wizard.message = { text: constants.ACCOUNT_STALE_ERROR(this.migrationStateModel._azureAccount) };
|
this.wizard.message = {
|
||||||
|
level: azdata.window.MessageLevel.Error,
|
||||||
|
text: constants.ACCOUNT_STALE_ERROR(this.migrationStateModel._azureAccount),
|
||||||
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.wizard.message = { text: constants.ACCOUNT_ACCESS_ERROR(this.migrationStateModel._azureAccount, error) };
|
this.wizard.message = {
|
||||||
|
level: azdata.window.MessageLevel.Error,
|
||||||
|
text: constants.ACCOUNT_ACCESS_ERROR(this.migrationStateModel._azureAccount, error),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}).component();
|
}).component();
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.MigrationMode) {
|
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.MigrationMode)) {
|
||||||
this._networkSharePath.value = this.migrationStateModel.savedInfo.networkShare?.networkShareLocation;
|
this._networkSharePath.value = this.migrationStateModel.savedInfo.networkShare?.networkShareLocation;
|
||||||
}
|
}
|
||||||
this._disposables.push(this._networkSharePath.onTextChanged(async (value) => {
|
this._disposables.push(this._networkSharePath.onTextChanged(async (value) => {
|
||||||
@@ -331,7 +331,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}).component();
|
}).component();
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup) {
|
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup)) {
|
||||||
this._windowsUserAccountText.value = this.migrationStateModel.savedInfo.networkShare?.windowsUser;
|
this._windowsUserAccountText.value = this.migrationStateModel.savedInfo.networkShare?.windowsUser;
|
||||||
}
|
}
|
||||||
this._disposables.push(this._windowsUserAccountText.onTextChanged((value) => {
|
this._disposables.push(this._windowsUserAccountText.onTextChanged((value) => {
|
||||||
@@ -458,7 +458,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
return flexContainer;
|
return flexContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private createTargetDatabaseContainer(): azdata.FlexContainer {
|
private createTargetDatabaseContainer(): azdata.FlexContainer {
|
||||||
const headerCssStyles: azdata.CssStyles = {
|
const headerCssStyles: azdata.CssStyles = {
|
||||||
...styles.LABEL_CSS,
|
...styles.LABEL_CSS,
|
||||||
@@ -755,253 +754,265 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||||
if (this.migrationStateModel.refreshDatabaseBackupPage) {
|
if (this.migrationStateModel.refreshDatabaseBackupPage) {
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup) {
|
try {
|
||||||
this.migrationStateModel._migrationDbs = this.migrationStateModel.savedInfo.databaseList;
|
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup)) {
|
||||||
}
|
this.migrationStateModel._migrationDbs = this.migrationStateModel.savedInfo.databaseList;
|
||||||
const isOfflineMigration = this.migrationStateModel._databaseBackup?.migrationMode === MigrationMode.OFFLINE;
|
}
|
||||||
const lastBackupFileColumnIndex = this._blobContainerTargetDatabaseNamesTable.columns.length - 1;
|
const isOfflineMigration = this.migrationStateModel._databaseBackup?.migrationMode === MigrationMode.OFFLINE;
|
||||||
this._blobContainerTargetDatabaseNamesTable.columns[lastBackupFileColumnIndex].hidden = !isOfflineMigration;
|
const lastBackupFileColumnIndex = this._blobContainerTargetDatabaseNamesTable.columns.length - 1;
|
||||||
this._blobContainerTargetDatabaseNamesTable.columns.forEach(column => {
|
this._blobContainerTargetDatabaseNamesTable.columns[lastBackupFileColumnIndex].hidden = !isOfflineMigration;
|
||||||
column.width = isOfflineMigration ? WIZARD_TABLE_COLUMN_WIDTH_SMALL : WIZARD_TABLE_COLUMN_WIDTH;
|
this._blobContainerTargetDatabaseNamesTable.columns.forEach(column => {
|
||||||
});
|
column.width = isOfflineMigration ? WIZARD_TABLE_COLUMN_WIDTH_SMALL : WIZARD_TABLE_COLUMN_WIDTH;
|
||||||
|
});
|
||||||
|
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.MigrationMode) {
|
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.MigrationMode)) {
|
||||||
if (this.migrationStateModel.savedInfo.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
|
if (this.migrationStateModel.savedInfo.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
|
||||||
this._networkShareButton.checked = true;
|
this._networkShareButton.checked = true;
|
||||||
|
} else {
|
||||||
|
this._networkShareButton.checked = false;
|
||||||
|
this._networkTableContainer.display = 'none';
|
||||||
|
await this._networkShareContainer.updateCssStyles({ 'display': 'none' });
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this._networkShareButton.checked = false;
|
this._networkShareButton.checked = false;
|
||||||
this._networkTableContainer.display = 'none';
|
this._networkTableContainer.display = 'none';
|
||||||
await this._networkShareContainer.updateCssStyles({ 'display': 'none' });
|
await this._networkShareContainer.updateCssStyles({ 'display': 'none' });
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this._networkShareButton.checked = false;
|
|
||||||
this._networkTableContainer.display = 'none';
|
|
||||||
await this._networkShareContainer.updateCssStyles({ 'display': 'none' });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.MigrationMode) {
|
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.MigrationMode)) {
|
||||||
if (this.migrationStateModel.savedInfo.networkContainerType === NetworkContainerType.BLOB_CONTAINER) {
|
if (this.migrationStateModel.savedInfo.networkContainerType === NetworkContainerType.BLOB_CONTAINER) {
|
||||||
this._blobContainerButton.checked = true;
|
this._blobContainerButton.checked = true;
|
||||||
|
} else {
|
||||||
|
this._blobContainerButton.checked = false;
|
||||||
|
this._blobTableContainer.display = 'none';
|
||||||
|
await this._blobContainer.updateCssStyles({ 'display': 'none' });
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this._blobContainerButton.checked = false;
|
this._blobContainerButton.checked = false;
|
||||||
this._blobTableContainer.display = 'none';
|
this._blobTableContainer.display = 'none';
|
||||||
await this._blobContainer.updateCssStyles({ 'display': 'none' });
|
await this._blobContainer.updateCssStyles({ 'display': 'none' });
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this._blobContainerButton.checked = false;
|
|
||||||
this._blobTableContainer.display = 'none';
|
|
||||||
await this._blobContainer.updateCssStyles({ 'display': 'none' });
|
|
||||||
}
|
|
||||||
|
|
||||||
await this._targetDatabaseContainer.updateCssStyles({ 'display': 'none' });
|
await this._targetDatabaseContainer.updateCssStyles({ 'display': 'none' });
|
||||||
await this._networkShareStorageAccountDetails.updateCssStyles({ 'display': 'none' });
|
await this._networkShareStorageAccountDetails.updateCssStyles({ 'display': 'none' });
|
||||||
const connectionProfile = await this.migrationStateModel.getSourceConnectionProfile();
|
const connectionProfile = await this.migrationStateModel.getSourceConnectionProfile();
|
||||||
const queryProvider = azdata.dataprotocol.getProvider<azdata.QueryProvider>((await this.migrationStateModel.getSourceConnectionProfile()).providerId, azdata.DataProviderType.QueryProvider);
|
const queryProvider = azdata.dataprotocol.getProvider<azdata.QueryProvider>((await this.migrationStateModel.getSourceConnectionProfile()).providerId, azdata.DataProviderType.QueryProvider);
|
||||||
const query = 'select SUSER_NAME()';
|
const query = 'select SUSER_NAME()';
|
||||||
const results = await queryProvider.runQueryAndReturn(await (azdata.connection.getUriForConnection(this.migrationStateModel.sourceConnectionId)), query);
|
const results = await queryProvider.runQueryAndReturn(await (azdata.connection.getUriForConnection(this.migrationStateModel.sourceConnectionId)), query);
|
||||||
const username = results.rows[0][0].displayValue;
|
const username = results.rows[0][0].displayValue;
|
||||||
this.migrationStateModel._authenticationType = connectionProfile.authenticationType === 'SqlLogin' ? MigrationSourceAuthenticationType.Sql : connectionProfile.authenticationType === 'Integrated' ? MigrationSourceAuthenticationType.Integrated : undefined!;
|
this.migrationStateModel._authenticationType = connectionProfile.authenticationType === 'SqlLogin' ? MigrationSourceAuthenticationType.Sql : connectionProfile.authenticationType === 'Integrated' ? MigrationSourceAuthenticationType.Integrated : undefined!;
|
||||||
this._sourceHelpText.value = constants.SQL_SOURCE_DETAILS(this.migrationStateModel._authenticationType, connectionProfile.serverName);
|
this._sourceHelpText.value = constants.SQL_SOURCE_DETAILS(this.migrationStateModel._authenticationType, connectionProfile.serverName);
|
||||||
this._sqlSourceUsernameInput.value = username;
|
this._sqlSourceUsernameInput.value = username;
|
||||||
this._sqlSourcePassword.value = (await azdata.connection.getCredentials(this.migrationStateModel.sourceConnectionId)).password;
|
this._sqlSourcePassword.value = (await azdata.connection.getCredentials(this.migrationStateModel.sourceConnectionId)).password;
|
||||||
|
|
||||||
this._networkShareTargetDatabaseNames = [];
|
this._networkShareTargetDatabaseNames = [];
|
||||||
this._blobContainerTargetDatabaseNames = [];
|
this._blobContainerTargetDatabaseNames = [];
|
||||||
this._blobContainerResourceGroupDropdowns = [];
|
this._blobContainerResourceGroupDropdowns = [];
|
||||||
this._blobContainerStorageAccountDropdowns = [];
|
this._blobContainerStorageAccountDropdowns = [];
|
||||||
this._blobContainerDropdowns = [];
|
this._blobContainerDropdowns = [];
|
||||||
this._blobContainerLastBackupFileDropdowns = [];
|
this._blobContainerLastBackupFileDropdowns = [];
|
||||||
|
|
||||||
if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI) {
|
if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI) {
|
||||||
this._existingDatabases = await this.migrationStateModel.getManagedDatabases();
|
this._existingDatabases = await this.migrationStateModel.getManagedDatabases();
|
||||||
}
|
|
||||||
this.migrationStateModel._targetDatabaseNames = [];
|
|
||||||
this.migrationStateModel._databaseBackup.blobs = [];
|
|
||||||
this.migrationStateModel._migrationDbs.forEach((db, index) => {
|
|
||||||
this.migrationStateModel._targetDatabaseNames.push('');
|
|
||||||
this.migrationStateModel._databaseBackup.blobs.push(<Blob>{});
|
|
||||||
const targetDatabaseInput = this._view.modelBuilder.inputBox().withProps({
|
|
||||||
required: true,
|
|
||||||
value: db,
|
|
||||||
width: WIZARD_TABLE_COLUMN_WIDTH
|
|
||||||
}).withValidation(c => {
|
|
||||||
if (this._networkShareTargetDatabaseNames.filter(t => t.value === c.value).length > 1) { //Making sure no databases have duplicate values.
|
|
||||||
c.validationErrorMessage = constants.DUPLICATE_NAME_ERROR;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI && this._existingDatabases.includes(c.value!)) { // Making sure if database with same name is not present on the target Azure SQL
|
|
||||||
c.validationErrorMessage = constants.DATABASE_ALREADY_EXISTS_MI(c.value!, this.migrationStateModel._targetServerInstance.name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (c.value!.length < 1 || c.value!.length > 128 || !/[^<>*%&:\\\/?]/.test(c.value!)) {
|
|
||||||
c.validationErrorMessage = constants.INVALID_TARGET_NAME_ERROR;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}).component();
|
|
||||||
this._disposables.push(targetDatabaseInput.onTextChanged(async (value) => {
|
|
||||||
this.migrationStateModel._targetDatabaseNames[index] = value.trim();
|
|
||||||
await this.validateFields();
|
|
||||||
}));
|
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup) {
|
|
||||||
targetDatabaseInput.value = this.migrationStateModel.savedInfo.targetDatabaseNames[index];
|
|
||||||
} else {
|
|
||||||
targetDatabaseInput.value = this.migrationStateModel._targetDatabaseNames[index];
|
|
||||||
}
|
}
|
||||||
this._networkShareTargetDatabaseNames.push(targetDatabaseInput);
|
this.migrationStateModel._targetDatabaseNames = [];
|
||||||
|
this.migrationStateModel._databaseBackup.blobs = [];
|
||||||
const blobTargetDatabaseInput = this._view.modelBuilder.inputBox().withProps({
|
this.migrationStateModel._migrationDbs.forEach((db, index) => {
|
||||||
required: true,
|
this.migrationStateModel._targetDatabaseNames.push('');
|
||||||
value: db,
|
this.migrationStateModel._databaseBackup.blobs.push(<Blob>{});
|
||||||
}).withValidation(c => {
|
const targetDatabaseInput = this._view.modelBuilder.inputBox().withProps({
|
||||||
if (this._blobContainerTargetDatabaseNames.filter(t => t.value === c.value).length > 1) { //Making sure no databases have duplicate values.
|
required: true,
|
||||||
c.validationErrorMessage = constants.DUPLICATE_NAME_ERROR;
|
value: db,
|
||||||
return false;
|
width: WIZARD_TABLE_COLUMN_WIDTH
|
||||||
}
|
}).withValidation(c => {
|
||||||
if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI && this._existingDatabases.includes(c.value!)) { // Making sure if database with same name is not present on the target Azure SQL
|
if (this._networkShareTargetDatabaseNames.filter(t => t.value === c.value).length > 1) { //Making sure no databases have duplicate values.
|
||||||
c.validationErrorMessage = constants.DATABASE_ALREADY_EXISTS_MI(c.value!, this.migrationStateModel._targetServerInstance.name);
|
c.validationErrorMessage = constants.DUPLICATE_NAME_ERROR;
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
if (c.value!.length < 1 || c.value!.length > 128 || !/[^<>*%&:\\\/?]/.test(c.value!)) {
|
|
||||||
c.validationErrorMessage = constants.INVALID_TARGET_NAME_ERROR;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}).component();
|
|
||||||
this._disposables.push(blobTargetDatabaseInput.onTextChanged((value) => {
|
|
||||||
this.migrationStateModel._targetDatabaseNames[index] = value.trim();
|
|
||||||
}));
|
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup) {
|
|
||||||
blobTargetDatabaseInput.value = this.migrationStateModel.savedInfo.targetDatabaseNames[index];
|
|
||||||
} else {
|
|
||||||
targetDatabaseInput.value = this.migrationStateModel._targetDatabaseNames[index];
|
|
||||||
}
|
|
||||||
this._blobContainerTargetDatabaseNames.push(blobTargetDatabaseInput);
|
|
||||||
|
|
||||||
const blobContainerResourceDropdown = this._view.modelBuilder.dropDown().withProps({
|
|
||||||
ariaLabel: constants.BLOB_CONTAINER_RESOURCE_GROUP,
|
|
||||||
editable: true,
|
|
||||||
fireOnTextChange: true,
|
|
||||||
required: true,
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
const blobContainerStorageAccountDropdown = this._view.modelBuilder.dropDown().withProps({
|
|
||||||
ariaLabel: constants.BLOB_CONTAINER_STORAGE_ACCOUNT,
|
|
||||||
editable: true,
|
|
||||||
fireOnTextChange: true,
|
|
||||||
required: true,
|
|
||||||
enabled: false,
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
const blobContainerDropdown = this._view.modelBuilder.dropDown().withProps({
|
|
||||||
ariaLabel: constants.BLOB_CONTAINER,
|
|
||||||
editable: true,
|
|
||||||
fireOnTextChange: true,
|
|
||||||
required: true,
|
|
||||||
enabled: false,
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
const blobContainerLastBackupFileDropdown = this._view.modelBuilder.dropDown().withProps({
|
|
||||||
ariaLabel: constants.BLOB_CONTAINER_LAST_BACKUP_FILE,
|
|
||||||
editable: true,
|
|
||||||
fireOnTextChange: true,
|
|
||||||
required: true,
|
|
||||||
enabled: false,
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
this._disposables.push(blobContainerResourceDropdown.onValueChanged(async (value) => {
|
|
||||||
const selectedIndex = findDropDownItemIndex(blobContainerResourceDropdown, value);
|
|
||||||
if (selectedIndex > -1 && !blobResourceGroupErrorStrings.includes(value)) {
|
|
||||||
this.migrationStateModel._databaseBackup.blobs[index].resourceGroup = this.migrationStateModel.getAzureResourceGroup(selectedIndex);
|
|
||||||
await this.loadBlobStorageDropdown(index);
|
|
||||||
await blobContainerStorageAccountDropdown.updateProperties({ enabled: true });
|
|
||||||
} else {
|
|
||||||
await this.disableBlobTableDropdowns(index, constants.RESOURCE_GROUP);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
this._blobContainerResourceGroupDropdowns.push(blobContainerResourceDropdown);
|
|
||||||
|
|
||||||
this._disposables.push(blobContainerStorageAccountDropdown.onValueChanged(async (value) => {
|
|
||||||
const selectedIndex = findDropDownItemIndex(blobContainerStorageAccountDropdown, value);
|
|
||||||
if (selectedIndex > -1 && !blobStorageAccountErrorStrings.includes(value)) {
|
|
||||||
this.migrationStateModel._databaseBackup.blobs[index].storageAccount = this.migrationStateModel.getStorageAccount(selectedIndex);
|
|
||||||
await this.loadBlobContainerDropdown(index);
|
|
||||||
await blobContainerDropdown.updateProperties({ enabled: true });
|
|
||||||
} else {
|
|
||||||
await this.disableBlobTableDropdowns(index, constants.STORAGE_ACCOUNT);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
this._blobContainerStorageAccountDropdowns.push(blobContainerStorageAccountDropdown);
|
|
||||||
|
|
||||||
this._disposables.push(blobContainerDropdown.onValueChanged(async (value) => {
|
|
||||||
const selectedIndex = findDropDownItemIndex(blobContainerDropdown, value);
|
|
||||||
if (selectedIndex > -1 && !blobContainerErrorStrings.includes(value)) {
|
|
||||||
this.migrationStateModel._databaseBackup.blobs[index].blobContainer = this.migrationStateModel.getBlobContainer(selectedIndex);
|
|
||||||
if (this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.OFFLINE) {
|
|
||||||
await this.loadBlobLastBackupFileDropdown(index);
|
|
||||||
await blobContainerLastBackupFileDropdown.updateProperties({ enabled: true });
|
|
||||||
}
|
}
|
||||||
|
if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI && this._existingDatabases.includes(c.value!)) { // Making sure if database with same name is not present on the target Azure SQL
|
||||||
|
c.validationErrorMessage = constants.DATABASE_ALREADY_EXISTS_MI(c.value!, this.migrationStateModel._targetServerInstance.name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c.value!.length < 1 || c.value!.length > 128 || !/[^<>*%&:\\\/?]/.test(c.value!)) {
|
||||||
|
c.validationErrorMessage = constants.INVALID_TARGET_NAME_ERROR;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}).component();
|
||||||
|
this._disposables.push(targetDatabaseInput.onTextChanged(async (value) => {
|
||||||
|
this.migrationStateModel._targetDatabaseNames[index] = value.trim();
|
||||||
|
await this.validateFields();
|
||||||
|
}));
|
||||||
|
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup)) {
|
||||||
|
targetDatabaseInput.value = this.migrationStateModel.savedInfo.targetDatabaseNames[index];
|
||||||
} else {
|
} else {
|
||||||
await this.disableBlobTableDropdowns(index, constants.BLOB_CONTAINER);
|
targetDatabaseInput.value = this.migrationStateModel._targetDatabaseNames[index];
|
||||||
}
|
}
|
||||||
}));
|
this._networkShareTargetDatabaseNames.push(targetDatabaseInput);
|
||||||
this._blobContainerDropdowns.push(blobContainerDropdown);
|
|
||||||
|
|
||||||
if (this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.OFFLINE) {
|
const blobTargetDatabaseInput = this._view.modelBuilder.inputBox().withProps({
|
||||||
this._disposables.push(blobContainerLastBackupFileDropdown.onValueChanged(value => {
|
required: true,
|
||||||
const selectedIndex = findDropDownItemIndex(blobContainerLastBackupFileDropdown, value);
|
value: db,
|
||||||
if (selectedIndex > -1 && !blobFileErrorStrings.includes(value)) {
|
}).withValidation(c => {
|
||||||
this.migrationStateModel._databaseBackup.blobs[index].lastBackupFile = this.migrationStateModel.getBlobLastBackupFileName(selectedIndex);
|
if (this._blobContainerTargetDatabaseNames.filter(t => t.value === c.value).length > 1) { //Making sure no databases have duplicate values.
|
||||||
|
c.validationErrorMessage = constants.DUPLICATE_NAME_ERROR;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI && this._existingDatabases.includes(c.value!)) { // Making sure if database with same name is not present on the target Azure SQL
|
||||||
|
c.validationErrorMessage = constants.DATABASE_ALREADY_EXISTS_MI(c.value!, this.migrationStateModel._targetServerInstance.name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (c.value!.length < 1 || c.value!.length > 128 || !/[^<>*%&:\\\/?]/.test(c.value!)) {
|
||||||
|
c.validationErrorMessage = constants.INVALID_TARGET_NAME_ERROR;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}).component();
|
||||||
|
this._disposables.push(blobTargetDatabaseInput.onTextChanged((value) => {
|
||||||
|
this.migrationStateModel._targetDatabaseNames[index] = value.trim();
|
||||||
|
}));
|
||||||
|
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup)) {
|
||||||
|
blobTargetDatabaseInput.value = this.migrationStateModel.savedInfo.targetDatabaseNames[index];
|
||||||
|
} else {
|
||||||
|
targetDatabaseInput.value = this.migrationStateModel._targetDatabaseNames[index];
|
||||||
|
}
|
||||||
|
this._blobContainerTargetDatabaseNames.push(blobTargetDatabaseInput);
|
||||||
|
|
||||||
|
const blobContainerResourceDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||||
|
ariaLabel: constants.BLOB_CONTAINER_RESOURCE_GROUP,
|
||||||
|
editable: true,
|
||||||
|
fireOnTextChange: true,
|
||||||
|
required: true,
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
const blobContainerStorageAccountDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||||
|
ariaLabel: constants.BLOB_CONTAINER_STORAGE_ACCOUNT,
|
||||||
|
editable: true,
|
||||||
|
fireOnTextChange: true,
|
||||||
|
required: true,
|
||||||
|
enabled: false,
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
const blobContainerDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||||
|
ariaLabel: constants.BLOB_CONTAINER,
|
||||||
|
editable: true,
|
||||||
|
fireOnTextChange: true,
|
||||||
|
required: true,
|
||||||
|
enabled: false,
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
const blobContainerLastBackupFileDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||||
|
ariaLabel: constants.BLOB_CONTAINER_LAST_BACKUP_FILE,
|
||||||
|
editable: true,
|
||||||
|
fireOnTextChange: true,
|
||||||
|
required: true,
|
||||||
|
enabled: false,
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this._disposables.push(blobContainerResourceDropdown.onValueChanged(async (value) => {
|
||||||
|
const selectedIndex = findDropDownItemIndex(blobContainerResourceDropdown, value);
|
||||||
|
if (selectedIndex > -1 && !blobResourceGroupErrorStrings.includes(value)) {
|
||||||
|
this.migrationStateModel._databaseBackup.blobs[index].resourceGroup = this.migrationStateModel.getAzureResourceGroup(selectedIndex);
|
||||||
|
await this.loadBlobStorageDropdown(index);
|
||||||
|
await blobContainerStorageAccountDropdown.updateProperties({ enabled: true });
|
||||||
|
} else {
|
||||||
|
await this.disableBlobTableDropdowns(index, constants.RESOURCE_GROUP);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
this._blobContainerLastBackupFileDropdowns.push(blobContainerLastBackupFileDropdown);
|
this._blobContainerResourceGroupDropdowns.push(blobContainerResourceDropdown);
|
||||||
|
|
||||||
|
this._disposables.push(blobContainerStorageAccountDropdown.onValueChanged(async (value) => {
|
||||||
|
const selectedIndex = findDropDownItemIndex(blobContainerStorageAccountDropdown, value);
|
||||||
|
if (selectedIndex > -1 && !blobStorageAccountErrorStrings.includes(value)) {
|
||||||
|
this.migrationStateModel._databaseBackup.blobs[index].storageAccount = this.migrationStateModel.getStorageAccount(selectedIndex);
|
||||||
|
await this.loadBlobContainerDropdown(index);
|
||||||
|
await blobContainerDropdown.updateProperties({ enabled: true });
|
||||||
|
} else {
|
||||||
|
await this.disableBlobTableDropdowns(index, constants.STORAGE_ACCOUNT);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
this._blobContainerStorageAccountDropdowns.push(blobContainerStorageAccountDropdown);
|
||||||
|
|
||||||
|
this._disposables.push(blobContainerDropdown.onValueChanged(async (value) => {
|
||||||
|
const selectedIndex = findDropDownItemIndex(blobContainerDropdown, value);
|
||||||
|
if (selectedIndex > -1 && !blobContainerErrorStrings.includes(value)) {
|
||||||
|
this.migrationStateModel._databaseBackup.blobs[index].blobContainer = this.migrationStateModel.getBlobContainer(selectedIndex);
|
||||||
|
if (this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.OFFLINE) {
|
||||||
|
await this.loadBlobLastBackupFileDropdown(index);
|
||||||
|
await blobContainerLastBackupFileDropdown.updateProperties({ enabled: true });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await this.disableBlobTableDropdowns(index, constants.BLOB_CONTAINER);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
this._blobContainerDropdowns.push(blobContainerDropdown);
|
||||||
|
|
||||||
|
if (this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.OFFLINE) {
|
||||||
|
this._disposables.push(blobContainerLastBackupFileDropdown.onValueChanged(value => {
|
||||||
|
const selectedIndex = findDropDownItemIndex(blobContainerLastBackupFileDropdown, value);
|
||||||
|
if (selectedIndex > -1 && !blobFileErrorStrings.includes(value)) {
|
||||||
|
this.migrationStateModel._databaseBackup.blobs[index].lastBackupFile = this.migrationStateModel.getBlobLastBackupFileName(selectedIndex);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
this._blobContainerLastBackupFileDropdowns.push(blobContainerLastBackupFileDropdown);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let data: azdata.DeclarativeTableCellValue[][] = [];
|
||||||
|
this.migrationStateModel._migrationDbs.forEach((db, index) => {
|
||||||
|
const targetRow: azdata.DeclarativeTableCellValue[] = [];
|
||||||
|
targetRow.push({
|
||||||
|
value: db
|
||||||
|
});
|
||||||
|
targetRow.push({
|
||||||
|
value: this._networkShareTargetDatabaseNames[index]
|
||||||
|
});
|
||||||
|
data.push(targetRow);
|
||||||
|
});
|
||||||
|
this._networkShareTargetDatabaseNamesTable.dataValues = data;
|
||||||
|
|
||||||
|
data = [];
|
||||||
|
this.migrationStateModel._migrationDbs.forEach((db, index) => {
|
||||||
|
const targetRow: azdata.DeclarativeTableCellValue[] = [];
|
||||||
|
targetRow.push({
|
||||||
|
value: db
|
||||||
|
});
|
||||||
|
targetRow.push({
|
||||||
|
value: this._blobContainerTargetDatabaseNames[index]
|
||||||
|
});
|
||||||
|
targetRow.push({
|
||||||
|
value: this._blobContainerResourceGroupDropdowns[index]
|
||||||
|
});
|
||||||
|
targetRow.push({
|
||||||
|
value: this._blobContainerStorageAccountDropdowns[index]
|
||||||
|
});
|
||||||
|
targetRow.push({
|
||||||
|
value: this._blobContainerDropdowns[index]
|
||||||
|
});
|
||||||
|
targetRow.push({
|
||||||
|
value: this._blobContainerLastBackupFileDropdowns[index]
|
||||||
|
});
|
||||||
|
data.push(targetRow);
|
||||||
|
});
|
||||||
|
await this._blobContainerTargetDatabaseNamesTable.setDataValues(data);
|
||||||
|
|
||||||
|
await this.getSubscriptionValues();
|
||||||
|
this.migrationStateModel.refreshDatabaseBackupPage = false;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
let errorText = error?.message;
|
||||||
|
if (errorText === constants.INVALID_OWNER_URI) {
|
||||||
|
errorText = constants.DATABASE_BACKUP_PAGE_LOAD_ERROR;
|
||||||
}
|
}
|
||||||
});
|
this.wizard.message = {
|
||||||
|
text: errorText,
|
||||||
|
description: error?.stack,
|
||||||
let data: azdata.DeclarativeTableCellValue[][] = [];
|
level: azdata.window.MessageLevel.Error
|
||||||
this.migrationStateModel._migrationDbs.forEach((db, index) => {
|
};
|
||||||
const targetRow: azdata.DeclarativeTableCellValue[] = [];
|
}
|
||||||
targetRow.push({
|
|
||||||
value: db
|
|
||||||
});
|
|
||||||
targetRow.push({
|
|
||||||
value: this._networkShareTargetDatabaseNames[index]
|
|
||||||
});
|
|
||||||
data.push(targetRow);
|
|
||||||
});
|
|
||||||
this._networkShareTargetDatabaseNamesTable.dataValues = data;
|
|
||||||
|
|
||||||
data = [];
|
|
||||||
this.migrationStateModel._migrationDbs.forEach((db, index) => {
|
|
||||||
const targetRow: azdata.DeclarativeTableCellValue[] = [];
|
|
||||||
targetRow.push({
|
|
||||||
value: db
|
|
||||||
});
|
|
||||||
targetRow.push({
|
|
||||||
value: this._blobContainerTargetDatabaseNames[index]
|
|
||||||
});
|
|
||||||
targetRow.push({
|
|
||||||
value: this._blobContainerResourceGroupDropdowns[index]
|
|
||||||
});
|
|
||||||
targetRow.push({
|
|
||||||
value: this._blobContainerStorageAccountDropdowns[index]
|
|
||||||
});
|
|
||||||
targetRow.push({
|
|
||||||
value: this._blobContainerDropdowns[index]
|
|
||||||
});
|
|
||||||
targetRow.push({
|
|
||||||
value: this._blobContainerLastBackupFileDropdowns[index]
|
|
||||||
});
|
|
||||||
data.push(targetRow);
|
|
||||||
});
|
|
||||||
await this._blobContainerTargetDatabaseNamesTable.setDataValues(data);
|
|
||||||
|
|
||||||
await this.getSubscriptionValues();
|
|
||||||
this.migrationStateModel.refreshDatabaseBackupPage = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||||
@@ -1131,7 +1142,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
this._blobTableContainer.display = (containerType === NetworkContainerType.BLOB_CONTAINER) ? 'inline' : 'none';
|
this._blobTableContainer.display = (containerType === NetworkContainerType.BLOB_CONTAINER) ? 'inline' : 'none';
|
||||||
|
|
||||||
//Preserving the database Names between the 2 tables.
|
//Preserving the database Names between the 2 tables.
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup) {
|
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup)) {
|
||||||
this.migrationStateModel._targetDatabaseNames = this.migrationStateModel.savedInfo.targetDatabaseNames;
|
this.migrationStateModel._targetDatabaseNames = this.migrationStateModel.savedInfo.targetDatabaseNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1150,7 +1161,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
await this.validateFields();
|
await this.validateFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async validateFields(): Promise<void> {
|
private async validateFields(): Promise<void> {
|
||||||
await this._sqlSourceUsernameInput.validate();
|
await this._sqlSourceUsernameInput.validate();
|
||||||
await this._sqlSourcePassword.validate();
|
await this._sqlSourcePassword.validate();
|
||||||
@@ -1175,7 +1185,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getSubscriptionValues(): Promise<void> {
|
private async getSubscriptionValues(): Promise<void> {
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup) {
|
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup)) {
|
||||||
this.migrationStateModel._targetSubscription = <azureResource.AzureResourceSubscription>this.migrationStateModel.savedInfo.targetSubscription;
|
this.migrationStateModel._targetSubscription = <azureResource.AzureResourceSubscription>this.migrationStateModel.savedInfo.targetSubscription;
|
||||||
this.migrationStateModel._targetServerInstance = <SqlManagedInstance | SqlVMServer>this.migrationStateModel.savedInfo.targetServerInstance;
|
this.migrationStateModel._targetServerInstance = <SqlManagedInstance | SqlVMServer>this.migrationStateModel.savedInfo.targetServerInstance;
|
||||||
}
|
}
|
||||||
@@ -1196,7 +1206,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
this._networkShareStorageAccountResourceGroupDropdown.loading = true;
|
this._networkShareStorageAccountResourceGroupDropdown.loading = true;
|
||||||
try {
|
try {
|
||||||
this._networkShareStorageAccountResourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._databaseBackup.subscription);
|
this._networkShareStorageAccountResourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._databaseBackup.subscription);
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup && this._networkShareStorageAccountResourceGroupDropdown.values) {
|
if (this.hasSavedInfo(NetworkContainerType.NETWORK_SHARE, this._networkShareStorageAccountResourceGroupDropdown.values)) {
|
||||||
this._networkShareStorageAccountResourceGroupDropdown.values.forEach((resource, index) => {
|
this._networkShareStorageAccountResourceGroupDropdown.values.forEach((resource, index) => {
|
||||||
if ((<azdata.CategoryValue>resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.networkShare?.resourceGroup?.id?.toLowerCase()) {
|
if ((<azdata.CategoryValue>resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.networkShare?.resourceGroup?.id?.toLowerCase()) {
|
||||||
selectDropDownIndex(this._networkShareStorageAccountResourceGroupDropdown, index);
|
selectDropDownIndex(this._networkShareStorageAccountResourceGroupDropdown, index);
|
||||||
@@ -1231,7 +1241,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
const resourceGroupValues = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._databaseBackup.subscription);
|
const resourceGroupValues = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._databaseBackup.subscription);
|
||||||
this._blobContainerResourceGroupDropdowns.forEach((dropDown, index) => {
|
this._blobContainerResourceGroupDropdowns.forEach((dropDown, index) => {
|
||||||
dropDown.values = resourceGroupValues;
|
dropDown.values = resourceGroupValues;
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup && dropDown.values) {
|
if (this.hasSavedInfo(NetworkContainerType.BLOB_CONTAINER, dropDown.values)) {
|
||||||
dropDown.values.forEach((resource, resourceIndex) => {
|
dropDown.values.forEach((resource, resourceIndex) => {
|
||||||
if ((<azdata.CategoryValue>resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.blobs[index]?.resourceGroup?.id?.toLowerCase()) {
|
if ((<azdata.CategoryValue>resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.blobs[index]?.resourceGroup?.id?.toLowerCase()) {
|
||||||
selectDropDownIndex(dropDown, resourceIndex);
|
selectDropDownIndex(dropDown, resourceIndex);
|
||||||
@@ -1251,8 +1261,8 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
private async loadBlobStorageDropdown(index: number): Promise<void> {
|
private async loadBlobStorageDropdown(index: number): Promise<void> {
|
||||||
this._blobContainerStorageAccountDropdowns[index].loading = true;
|
this._blobContainerStorageAccountDropdowns[index].loading = true;
|
||||||
try {
|
try {
|
||||||
this._blobContainerStorageAccountDropdowns[index].values = await this.migrationStateModel.getStorageAccountValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index].resourceGroup);
|
this._blobContainerStorageAccountDropdowns[index].values = await this.migrationStateModel.getStorageAccountValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index]?.resourceGroup);
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup && this._blobContainerStorageAccountDropdowns[index].values && this.migrationStateModel.savedInfo.blobs[index].storageAccount) {
|
if (this.hasSavedInfo(NetworkContainerType.BLOB_CONTAINER, this._blobContainerStorageAccountDropdowns[index].values && this.migrationStateModel.savedInfo.blobs[index]?.storageAccount)) {
|
||||||
this._blobContainerStorageAccountDropdowns[index].values!.forEach((resource, resourceIndex) => {
|
this._blobContainerStorageAccountDropdowns[index].values!.forEach((resource, resourceIndex) => {
|
||||||
if ((<azdata.CategoryValue>resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.blobs[index]?.storageAccount?.id?.toLowerCase()) {
|
if ((<azdata.CategoryValue>resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.blobs[index]?.storageAccount?.id?.toLowerCase()) {
|
||||||
selectDropDownIndex(this._blobContainerStorageAccountDropdowns[index], resourceIndex);
|
selectDropDownIndex(this._blobContainerStorageAccountDropdowns[index], resourceIndex);
|
||||||
@@ -1271,9 +1281,9 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
private async loadBlobContainerDropdown(index: number): Promise<void> {
|
private async loadBlobContainerDropdown(index: number): Promise<void> {
|
||||||
this._blobContainerDropdowns[index].loading = true;
|
this._blobContainerDropdowns[index].loading = true;
|
||||||
try {
|
try {
|
||||||
const blobContainerValues = await this.migrationStateModel.getBlobContainerValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index].storageAccount);
|
const blobContainerValues = await this.migrationStateModel.getBlobContainerValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index]?.storageAccount);
|
||||||
this._blobContainerDropdowns[index].values = blobContainerValues;
|
this._blobContainerDropdowns[index].values = blobContainerValues;
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup && this._blobContainerDropdowns[index].values && this.migrationStateModel.savedInfo.blobs[index].blobContainer) {
|
if (this.hasSavedInfo(NetworkContainerType.BLOB_CONTAINER, this._blobContainerDropdowns[index].values && this.migrationStateModel.savedInfo.blobs[index]?.blobContainer)) {
|
||||||
this._blobContainerDropdowns[index].values!.forEach((resource, resourceIndex) => {
|
this._blobContainerDropdowns[index].values!.forEach((resource, resourceIndex) => {
|
||||||
if ((<azdata.CategoryValue>resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.blobs[index]?.blobContainer?.id?.toLowerCase()) {
|
if ((<azdata.CategoryValue>resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.blobs[index]?.blobContainer?.id?.toLowerCase()) {
|
||||||
selectDropDownIndex(this._blobContainerDropdowns[index], resourceIndex);
|
selectDropDownIndex(this._blobContainerDropdowns[index], resourceIndex);
|
||||||
@@ -1292,9 +1302,9 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
private async loadBlobLastBackupFileDropdown(index: number): Promise<void> {
|
private async loadBlobLastBackupFileDropdown(index: number): Promise<void> {
|
||||||
this._blobContainerLastBackupFileDropdowns[index].loading = true;
|
this._blobContainerLastBackupFileDropdowns[index].loading = true;
|
||||||
try {
|
try {
|
||||||
const blobLastBackupFileValues = await this.migrationStateModel.getBlobLastBackupFileNameValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index].storageAccount, this.migrationStateModel._databaseBackup.blobs[index].blobContainer);
|
const blobLastBackupFileValues = await this.migrationStateModel.getBlobLastBackupFileNameValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index]?.storageAccount, this.migrationStateModel._databaseBackup.blobs[index]?.blobContainer);
|
||||||
this._blobContainerLastBackupFileDropdowns[index].values = blobLastBackupFileValues;
|
this._blobContainerLastBackupFileDropdowns[index].values = blobLastBackupFileValues;
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup && this._blobContainerLastBackupFileDropdowns[index].values && this.migrationStateModel.savedInfo.blobs[index].lastBackupFile) {
|
if (this.hasSavedInfo(NetworkContainerType.BLOB_CONTAINER, this._blobContainerLastBackupFileDropdowns[index].values && this.migrationStateModel.savedInfo.blobs[index]?.lastBackupFile)) {
|
||||||
this._blobContainerLastBackupFileDropdowns[index].values!.forEach((resource, resourceIndex) => {
|
this._blobContainerLastBackupFileDropdowns[index].values!.forEach((resource, resourceIndex) => {
|
||||||
if ((<azdata.CategoryValue>resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.blobs[index]?.lastBackupFile!.toLowerCase()) {
|
if ((<azdata.CategoryValue>resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.blobs[index]?.lastBackupFile!.toLowerCase()) {
|
||||||
selectDropDownIndex(this._blobContainerLastBackupFileDropdowns[index], resourceIndex);
|
selectDropDownIndex(this._blobContainerLastBackupFileDropdowns[index], resourceIndex);
|
||||||
@@ -1334,4 +1344,12 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
|||||||
selectDropDownIndex(this._blobContainerStorageAccountDropdowns[rowIndex], 0);
|
selectDropDownIndex(this._blobContainerStorageAccountDropdowns[rowIndex], 0);
|
||||||
await this._blobContainerStorageAccountDropdowns[rowIndex].updateProperties(dropdownProps);
|
await this._blobContainerStorageAccountDropdowns[rowIndex].updateProperties(dropdownProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private hasSavedInfo(networkContainerType: NetworkContainerType, values: any): boolean {
|
||||||
|
if (this.migrationStateModel._databaseBackup.networkContainerType === networkContainerType &&
|
||||||
|
(this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseBackup) && values)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||||
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
|
import { MigrationStateModel, Page, StateChangeEvent } from '../models/stateMachine';
|
||||||
import * as constants from '../constants/strings';
|
import * as constants from '../constants/strings';
|
||||||
import { IconPath, IconPathHelper } from '../constants/iconPathHelper';
|
import { IconPath, IconPathHelper } from '../constants/iconPathHelper';
|
||||||
import { debounce } from '../api/utils';
|
import { debounce } from '../api/utils';
|
||||||
@@ -275,17 +275,26 @@ export class DatabaseSelectorPage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
).component();
|
).component();
|
||||||
|
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= 1) {
|
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.DatabaseSelector) {
|
||||||
await this._databaseSelectorTable.setDataValues(this.migrationStateModel.savedInfo.selectedDatabases);
|
await this._databaseSelectorTable.setDataValues(this.migrationStateModel.savedInfo.selectedDatabases);
|
||||||
} else {
|
} else {
|
||||||
|
if (this.migrationStateModel.retryMigration) {
|
||||||
|
const sourceDatabaseName = this.migrationStateModel.savedInfo.databaseList[0];
|
||||||
|
this._databaseTableValues.forEach((row, index) => {
|
||||||
|
const dbName = row[1].value as string;
|
||||||
|
if (dbName?.toLowerCase() === sourceDatabaseName?.toLowerCase()) {
|
||||||
|
row[0].value = true;
|
||||||
|
} else {
|
||||||
|
row[0].enabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
await this._databaseSelectorTable.setDataValues(this._databaseTableValues);
|
await this._databaseSelectorTable.setDataValues(this._databaseTableValues);
|
||||||
|
await this.updateValuesOnSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._disposables.push(this._databaseSelectorTable.onDataChanged(async () => {
|
this._disposables.push(this._databaseSelectorTable.onDataChanged(async () => {
|
||||||
await this._dbCount.updateProperties({
|
await this.updateValuesOnSelection();
|
||||||
'value': constants.DATABASES_SELECTED(this.selectedDbs().length, this._databaseTableValues.length)
|
|
||||||
});
|
|
||||||
this.migrationStateModel._databaseAssessment = this.selectedDbs();
|
|
||||||
this.migrationStateModel.databaseSelectorTableValues = <azdata.DeclarativeTableCellValue[][]>this._databaseSelectorTable.dataValues;
|
|
||||||
}));
|
}));
|
||||||
const flex = view.modelBuilder.flexContainer().withLayout({
|
const flex = view.modelBuilder.flexContainer().withLayout({
|
||||||
flexFlow: 'column',
|
flexFlow: 'column',
|
||||||
@@ -314,6 +323,14 @@ export class DatabaseSelectorPage extends MigrationWizardPage {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async updateValuesOnSelection() {
|
||||||
|
await this._dbCount.updateProperties({
|
||||||
|
'value': constants.DATABASES_SELECTED(this.selectedDbs().length, this._databaseTableValues.length)
|
||||||
|
});
|
||||||
|
this.migrationStateModel._databaseAssessment = this.selectedDbs();
|
||||||
|
this.migrationStateModel.databaseSelectorTableValues = <azdata.DeclarativeTableCellValue[][]>this._databaseSelectorTable.dataValues;
|
||||||
|
}
|
||||||
|
|
||||||
// undo when bug #16445 is fixed
|
// undo when bug #16445 is fixed
|
||||||
private createIconTextCell(icon: IconPath, text: string): string {
|
private createIconTextCell(icon: IconPath, text: string): string {
|
||||||
return text;
|
return text;
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.IntegrationRuntime) {
|
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.IntegrationRuntime)) {
|
||||||
this.migrationStateModel._targetSubscription = <azureResource.AzureResourceSubscription>this.migrationStateModel.savedInfo.targetSubscription;
|
this.migrationStateModel._targetSubscription = <azureResource.AzureResourceSubscription>this.migrationStateModel.savedInfo.targetSubscription;
|
||||||
this.migrationStateModel._targetServerInstance = <SqlManagedInstance | SqlVMServer>this.migrationStateModel.savedInfo.targetServerInstance;
|
this.migrationStateModel._targetServerInstance = <SqlManagedInstance | SqlVMServer>this.migrationStateModel.savedInfo.targetServerInstance;
|
||||||
}
|
}
|
||||||
@@ -391,7 +391,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
this._resourceGroupDropdown.loading = true;
|
this._resourceGroupDropdown.loading = true;
|
||||||
try {
|
try {
|
||||||
this._resourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._targetSubscription);
|
this._resourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._targetSubscription);
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.IntegrationRuntime && this._resourceGroupDropdown.values) {
|
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.IntegrationRuntime && this._resourceGroupDropdown.values)) {
|
||||||
this._resourceGroupDropdown.values.forEach((resource, resourceIndex) => {
|
this._resourceGroupDropdown.values.forEach((resource, resourceIndex) => {
|
||||||
const resourceId = this.migrationStateModel.savedInfo?.migrationServiceId?.toLowerCase();
|
const resourceId = this.migrationStateModel.savedInfo?.migrationServiceId?.toLowerCase();
|
||||||
if (resourceId && (<azdata.CategoryValue>resource).name.toLowerCase() === getFullResourceGroupFromId(resourceId)) {
|
if (resourceId && (<azdata.CategoryValue>resource).name.toLowerCase() === getFullResourceGroupFromId(resourceId)) {
|
||||||
@@ -409,8 +409,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
|||||||
try {
|
try {
|
||||||
this._dmsDropdown.values = await this.migrationStateModel.getSqlMigrationServiceValues(this.migrationStateModel._targetSubscription, <SqlManagedInstance>this.migrationStateModel._targetServerInstance, resourceGroupName);
|
this._dmsDropdown.values = await this.migrationStateModel.getSqlMigrationServiceValues(this.migrationStateModel._targetSubscription, <SqlManagedInstance>this.migrationStateModel._targetServerInstance, resourceGroupName);
|
||||||
const selectedSqlMigrationService = this._dmsDropdown.values.find(v => v.displayName.toLowerCase() === this.migrationStateModel._sqlMigrationService?.name?.toLowerCase());
|
const selectedSqlMigrationService = this._dmsDropdown.values.find(v => v.displayName.toLowerCase() === this.migrationStateModel._sqlMigrationService?.name?.toLowerCase());
|
||||||
|
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.IntegrationRuntime && this._dmsDropdown.values)) {
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.IntegrationRuntime && this._dmsDropdown.values) {
|
|
||||||
this._dmsDropdown.values.forEach((resource, resourceIndex) => {
|
this._dmsDropdown.values.forEach((resource, resourceIndex) => {
|
||||||
if ((<azdata.CategoryValue>resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.migrationServiceId?.toLowerCase()) {
|
if ((<azdata.CategoryValue>resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.migrationServiceId?.toLowerCase()) {
|
||||||
selectDropDownIndex(this._dmsDropdown, resourceIndex);
|
selectDropDownIndex(this._dmsDropdown, resourceIndex);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||||
import { MigrationMode, MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
|
import { MigrationMode, MigrationStateModel, Page, StateChangeEvent } from '../models/stateMachine';
|
||||||
import * as constants from '../constants/strings';
|
import * as constants from '../constants/strings';
|
||||||
import * as styles from '../constants/styles';
|
import * as styles from '../constants/styles';
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ export class MigrationModePage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= 3) {
|
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.MigrationMode)) {
|
||||||
if (this.migrationStateModel.savedInfo.migrationMode === MigrationMode.ONLINE) {
|
if (this.migrationStateModel.savedInfo.migrationMode === MigrationMode.ONLINE) {
|
||||||
onlineButton.checked = true;
|
onlineButton.checked = true;
|
||||||
offlineButton.checked = false;
|
offlineButton.checked = false;
|
||||||
|
|||||||
@@ -197,6 +197,14 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
await this._view.initializeModel(this._rootContainer);
|
await this._view.initializeModel(this._rootContainer);
|
||||||
|
|
||||||
|
if (this.hasSavedInfo()) {
|
||||||
|
if (this.migrationStateModel.savedInfo.migrationTargetType === MigrationTargetType.SQLMI) {
|
||||||
|
this.migrationStateModel._miDbs = this.migrationStateModel.savedInfo.databaseList;
|
||||||
|
} else {
|
||||||
|
this.migrationStateModel._vmDbs = this.migrationStateModel.savedInfo.databaseList;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private createStatusComponent(view: azdata.ModelView): azdata.TextComponent {
|
private createStatusComponent(view: azdata.ModelView): azdata.TextComponent {
|
||||||
@@ -300,7 +308,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
let serverName = '';
|
let serverName = '';
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.serverName) {
|
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.serverName)) {
|
||||||
serverName = this.migrationStateModel.serverName;
|
serverName = this.migrationStateModel.serverName;
|
||||||
} else {
|
} else {
|
||||||
serverName = (await this.migrationStateModel.getSourceConnectionProfile()).serverName;
|
serverName = (await this.migrationStateModel.getSourceConnectionProfile()).serverName;
|
||||||
@@ -505,7 +513,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
this.migrationStateModel._migrationDbs = miDbs;
|
this.migrationStateModel._migrationDbs = miDbs;
|
||||||
} else {
|
} else {
|
||||||
this._viewAssessmentsHelperText.value = constants.SKU_RECOMMENDATION_VIEW_ASSESSMENT_VM;
|
this._viewAssessmentsHelperText.value = constants.SKU_RECOMMENDATION_VIEW_ASSESSMENT_VM;
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.SKURecommendation) {
|
if ((this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.SKURecommendation)) {
|
||||||
this._databaseSelectedHelperText.value = constants.TOTAL_DATABASES_SELECTED(this.migrationStateModel.savedInfo.databaseList.length, this.migrationStateModel._databaseAssessment.length);
|
this._databaseSelectedHelperText.value = constants.TOTAL_DATABASES_SELECTED(this.migrationStateModel.savedInfo.databaseList.length, this.migrationStateModel._databaseAssessment.length);
|
||||||
} else {
|
} else {
|
||||||
this._databaseSelectedHelperText.value = constants.TOTAL_DATABASES_SELECTED(vmDbs.length, this.migrationStateModel._databaseAssessment.length);
|
this._databaseSelectedHelperText.value = constants.TOTAL_DATABASES_SELECTED(vmDbs.length, this.migrationStateModel._databaseAssessment.length);
|
||||||
@@ -540,12 +548,12 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
await this.migrationStateModel.getDatabaseAssessments(MigrationTargetType.SQLMI);
|
await this.migrationStateModel.getDatabaseAssessments(MigrationTargetType.SQLMI);
|
||||||
}
|
}
|
||||||
|
|
||||||
const assessmentError = this.migrationStateModel._assessmentResults.assessmentError;
|
const assessmentError = this.migrationStateModel._assessmentResults?.assessmentError;
|
||||||
if (assessmentError) {
|
if (assessmentError) {
|
||||||
errors.push(`message: ${assessmentError.message}${EOL}stack: ${assessmentError.stack}`);
|
errors.push(`message: ${assessmentError.message}${EOL}stack: ${assessmentError.stack}`);
|
||||||
}
|
}
|
||||||
if (this.migrationStateModel?._assessmentResults?.errors?.length! > 0) {
|
if (this.migrationStateModel?._assessmentResults?.errors?.length! > 0) {
|
||||||
errors.push(...this.migrationStateModel._assessmentResults.errors?.map(
|
errors.push(...this.migrationStateModel._assessmentResults?.errors?.map(
|
||||||
e => `message: ${e.message}${EOL}errorSummary: ${e.errorSummary}${EOL}possibleCauses: ${e.possibleCauses}${EOL}guidance: ${e.guidance}${EOL}errorId: ${e.errorId}`)!);
|
e => `message: ${e.message}${EOL}errorSummary: ${e.errorSummary}${EOL}possibleCauses: ${e.possibleCauses}${EOL}guidance: ${e.guidance}${EOL}errorId: ${e.errorId}`)!);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,11 +574,11 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
} else {
|
} else {
|
||||||
this._assessmentStatusIcon.iconPath = IconPathHelper.completedMigration;
|
this._assessmentStatusIcon.iconPath = IconPathHelper.completedMigration;
|
||||||
this._igComponent.value = constants.ASSESSMENT_COMPLETED(serverName);
|
this._igComponent.value = constants.ASSESSMENT_COMPLETED(serverName);
|
||||||
this._detailsComponent.value = constants.SKU_RECOMMENDATION_ALL_SUCCESSFUL(this.migrationStateModel._assessmentResults.databaseAssessments.length);
|
this._detailsComponent.value = constants.SKU_RECOMMENDATION_ALL_SUCCESSFUL(this.migrationStateModel._assessmentResults?.databaseAssessments?.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((this.migrationStateModel.resumeAssessment) && this.migrationStateModel.savedInfo.closedPage >= Page.SKURecommendation) {
|
if (this.hasSavedInfo()) {
|
||||||
if (this.migrationStateModel.savedInfo.migrationTargetType) {
|
if (this.migrationStateModel.savedInfo.migrationTargetType) {
|
||||||
this._rbg.selectedCardId = this.migrationStateModel.savedInfo.migrationTargetType;
|
this._rbg.selectedCardId = this.migrationStateModel.savedInfo.migrationTargetType;
|
||||||
await this.refreshCardText();
|
await this.refreshCardText();
|
||||||
@@ -602,9 +610,9 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
await this.assessmentGroupContainer.updateCssStyles({ 'display': display });
|
await this.assessmentGroupContainer.updateCssStyles({ 'display': display });
|
||||||
this.assessmentGroupContainer.display = display;
|
this.assessmentGroupContainer.display = display;
|
||||||
|
|
||||||
display = this._rbg.selectedCardId
|
display = (this._rbg.selectedCardId
|
||||||
&& (!failedAssessment || this._skipAssessmentCheckbox.checked)
|
&& (!failedAssessment || this._skipAssessmentCheckbox.checked)
|
||||||
&& this.migrationStateModel._migrationDbs.length > 0
|
&& this.migrationStateModel._migrationDbs.length > 0)
|
||||||
? 'inline'
|
? 'inline'
|
||||||
: 'none';
|
: 'none';
|
||||||
await this._targetContainer.updateCssStyles({ 'display': display });
|
await this._targetContainer.updateCssStyles({ 'display': display });
|
||||||
@@ -614,7 +622,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async populateSubscriptionDropdown(): Promise<void> {
|
private async populateSubscriptionDropdown(): Promise<void> {
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.SKURecommendation) {
|
if (this.hasSavedInfo()) {
|
||||||
this.migrationStateModel._azureAccount = <azdata.Account>this.migrationStateModel.savedInfo.azureAccount;
|
this.migrationStateModel._azureAccount = <azdata.Account>this.migrationStateModel.savedInfo.azureAccount;
|
||||||
}
|
}
|
||||||
if (!this.migrationStateModel._targetSubscription) {
|
if (!this.migrationStateModel._targetSubscription) {
|
||||||
@@ -628,9 +636,9 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
this._managedInstanceSubscriptionDropdown.loading = false;
|
this._managedInstanceSubscriptionDropdown.loading = false;
|
||||||
this._resourceDropdown.loading = false;
|
this._resourceDropdown.loading = false;
|
||||||
}
|
}
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= 2 && this._managedInstanceSubscriptionDropdown.values) {
|
if (this.hasSavedInfo() && this._managedInstanceSubscriptionDropdown.values) {
|
||||||
this._managedInstanceSubscriptionDropdown.values.forEach((subscription, index) => {
|
this._managedInstanceSubscriptionDropdown.values!.forEach((subscription, index) => {
|
||||||
if ((<azdata.CategoryValue>subscription).name === this.migrationStateModel.savedInfo?.subscription?.id) {
|
if ((<azdata.CategoryValue>subscription).name.toLowerCase() === this.migrationStateModel.savedInfo?.subscription?.id.toLowerCase()) {
|
||||||
selectDropDownIndex(this._managedInstanceSubscriptionDropdown, index);
|
selectDropDownIndex(this._managedInstanceSubscriptionDropdown, index);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -645,9 +653,9 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
this._azureLocationDropdown.loading = true;
|
this._azureLocationDropdown.loading = true;
|
||||||
try {
|
try {
|
||||||
this._azureResourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._targetSubscription);
|
this._azureResourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._targetSubscription);
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= 2 && this._azureResourceGroupDropdown.values) {
|
if (this.hasSavedInfo() && this._azureResourceGroupDropdown.values) {
|
||||||
this._azureResourceGroupDropdown.values.forEach((resourceGroup, index) => {
|
this._azureResourceGroupDropdown.values.forEach((resourceGroup, index) => {
|
||||||
if (resourceGroup.name === this.migrationStateModel.savedInfo?.resourceGroup?.id) {
|
if (resourceGroup.name.toLowerCase() === this.migrationStateModel.savedInfo?.resourceGroup?.id.toLowerCase()) {
|
||||||
selectDropDownIndex(this._azureResourceGroupDropdown, index);
|
selectDropDownIndex(this._azureResourceGroupDropdown, index);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -655,7 +663,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
selectDropDownIndex(this._azureResourceGroupDropdown, 0);
|
selectDropDownIndex(this._azureResourceGroupDropdown, 0);
|
||||||
}
|
}
|
||||||
this._azureLocationDropdown.values = await this.migrationStateModel.getAzureLocationDropdownValues(this.migrationStateModel._targetSubscription);
|
this._azureLocationDropdown.values = await this.migrationStateModel.getAzureLocationDropdownValues(this.migrationStateModel._targetSubscription);
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= 2 && this._azureLocationDropdown.values) {
|
if (this.hasSavedInfo() && this._azureLocationDropdown.values) {
|
||||||
this._azureLocationDropdown.values.forEach((location, index) => {
|
this._azureLocationDropdown.values.forEach((location, index) => {
|
||||||
if (location.displayName === this.migrationStateModel.savedInfo?.location?.displayName) {
|
if (location.displayName === this.migrationStateModel.savedInfo?.location?.displayName) {
|
||||||
selectDropDownIndex(this._azureLocationDropdown, index);
|
selectDropDownIndex(this._azureLocationDropdown, index);
|
||||||
@@ -690,9 +698,9 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
this.migrationStateModel._location,
|
this.migrationStateModel._location,
|
||||||
this.migrationStateModel._resourceGroup);
|
this.migrationStateModel._resourceGroup);
|
||||||
}
|
}
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= 2 && this._resourceDropdown.values) {
|
if (this.hasSavedInfo() && this._resourceDropdown.values) {
|
||||||
this._resourceDropdown.values.forEach((resource, index) => {
|
this._resourceDropdown.values.forEach((resource, index) => {
|
||||||
if (resource.displayName === this.migrationStateModel.savedInfo?.targetServerInstance?.name) {
|
if (resource.displayName.toLowerCase() === this.migrationStateModel.savedInfo?.targetServerInstance?.name.toLowerCase()) {
|
||||||
selectDropDownIndex(this._resourceDropdown, index);
|
selectDropDownIndex(this._resourceDropdown, index);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -708,9 +716,6 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
|
|
||||||
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||||
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||||
if (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.SKURecommendation) {
|
|
||||||
this.migrationStateModel._migrationDbs = this.migrationStateModel.savedInfo.databaseList;
|
|
||||||
}
|
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
this.wizard.message = {
|
this.wizard.message = {
|
||||||
text: '',
|
text: '',
|
||||||
@@ -785,8 +790,8 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
this._targetContainer.display = (this.migrationStateModel._migrationDbs.length === 0) ? 'none' : 'inline';
|
this._targetContainer.display = (this.migrationStateModel._migrationDbs.length === 0) ? 'none' : 'inline';
|
||||||
|
|
||||||
if (this.migrationStateModel._assessmentResults) {
|
if (this.migrationStateModel._assessmentResults) {
|
||||||
const dbCount = this.migrationStateModel._assessmentResults.databaseAssessments?.length;
|
const dbCount = this.migrationStateModel._assessmentResults?.databaseAssessments?.length;
|
||||||
const dbWithoutIssuesCount = this.migrationStateModel._assessmentResults.databaseAssessments?.filter(db => db.issues?.length === 0).length;
|
const dbWithoutIssuesCount = this.migrationStateModel._assessmentResults?.databaseAssessments?.filter(db => db.issues?.length === 0).length;
|
||||||
this._rbg.cards[0].descriptions[1].textValue = constants.CAN_BE_MIGRATED(dbWithoutIssuesCount, dbCount);
|
this._rbg.cards[0].descriptions[1].textValue = constants.CAN_BE_MIGRATED(dbWithoutIssuesCount, dbCount);
|
||||||
this._rbg.cards[1].descriptions[1].textValue = constants.CAN_BE_MIGRATED(dbCount, dbCount);
|
this._rbg.cards[1].descriptions[1].textValue = constants.CAN_BE_MIGRATED(dbCount, dbCount);
|
||||||
|
|
||||||
@@ -837,6 +842,10 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
}).component();
|
}).component();
|
||||||
return this._assessmentInfo;
|
return this._assessmentInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private hasSavedInfo(): boolean {
|
||||||
|
return this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.SKURecommendation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ export class SummaryPage extends MigrationWizardPage {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE && this.migrationStateModel._nodeNames.length > 0) {
|
if (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE && this.migrationStateModel._nodeNames?.length > 0) {
|
||||||
this._flexContainer.addItem(createInformationRow(this._view, constants.SHIR, this.migrationStateModel._nodeNames.join(', ')));
|
this._flexContainer.addItem(createInformationRow(this._view, constants.SHIR, this.migrationStateModel._nodeNames.join(', ')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export class WizardController {
|
|||||||
const wizardSetupPromises: Thenable<void>[] = [];
|
const wizardSetupPromises: Thenable<void>[] = [];
|
||||||
wizardSetupPromises.push(...pages.map(p => p.registerWizardContent()));
|
wizardSetupPromises.push(...pages.map(p => p.registerWizardContent()));
|
||||||
wizardSetupPromises.push(this._wizardObject.open());
|
wizardSetupPromises.push(this._wizardObject.open());
|
||||||
if (this._model.resumeAssessment) {
|
if (this._model.retryMigration || this._model.resumeAssessment) {
|
||||||
if (this._model.savedInfo.closedPage >= Page.MigrationMode) {
|
if (this._model.savedInfo.closedPage >= Page.MigrationMode) {
|
||||||
this._model.refreshDatabaseBackupPage = true;
|
this._model.refreshDatabaseBackupPage = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "azuredatastudio",
|
"name": "azuredatastudio",
|
||||||
"version": "1.33.0",
|
"version": "1.33.0",
|
||||||
"distro": "01b299c0f340f6cf9dc10290b3aadea449c7940f",
|
"distro": "e39d1a2d41862fb0f5b4e8fc0886680e32ea5e27",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Microsoft Corporation"
|
"name": "Microsoft Corporation"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ const defaultOptions: IAutoColumnSizeOptions = {
|
|||||||
extraColumnHeaderWidth: 0
|
extraColumnHeaderWidth: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set the max number of rows to scan to 10 since the result grid viewport in query editor is usually around 10 rows but can go up to 20 rows for notebooks.
|
|
||||||
// In most cases, 10 rows are enough to get a reasonable width and will cut down measuring costs for large notebooks.
|
|
||||||
const MAX_ROWS_TO_SCAN = 10;
|
|
||||||
|
|
||||||
export class AutoColumnSize<T extends Slick.SlickData> implements Slick.Plugin<T> {
|
export class AutoColumnSize<T extends Slick.SlickData> implements Slick.Plugin<T> {
|
||||||
private _grid!: Slick.Grid<T>;
|
private _grid!: Slick.Grid<T>;
|
||||||
private _$container!: JQuery;
|
private _$container!: JQuery;
|
||||||
@@ -166,7 +162,7 @@ export class AutoColumnSize<T extends Slick.SlickData> implements Slick.Plugin<T
|
|||||||
let data = this._grid.getData() as Slick.DataProvider<T>;
|
let data = this._grid.getData() as Slick.DataProvider<T>;
|
||||||
let viewPort = this._grid.getViewport();
|
let viewPort = this._grid.getViewport();
|
||||||
let start = Math.max(0, viewPort.top);
|
let start = Math.max(0, viewPort.top);
|
||||||
let end = Math.min(data.getLength(), MAX_ROWS_TO_SCAN);
|
let end = Math.min(data.getLength(), viewPort.bottom);
|
||||||
let allTexts: Array<string>[] = [];
|
let allTexts: Array<string>[] = [];
|
||||||
let rowElements: JQuery[] = [];
|
let rowElements: JQuery[] = [];
|
||||||
|
|
||||||
@@ -183,6 +179,9 @@ export class AutoColumnSize<T extends Slick.SlickData> implements Slick.Plugin<T
|
|||||||
let templates = this.getMaxTextTemplates(allTexts, columnDefs, colIndices, data, rowElements);
|
let templates = this.getMaxTextTemplates(allTexts, columnDefs, colIndices, data, rowElements);
|
||||||
|
|
||||||
let widths = this.getTemplateWidths(rowElements, templates);
|
let widths = this.getTemplateWidths(rowElements, templates);
|
||||||
|
rowElements.forEach(rowElement => {
|
||||||
|
this.deleteRow(rowElement);
|
||||||
|
});
|
||||||
|
|
||||||
return widths.map((width) => Math.min(this._options.maxWidth, width));
|
return widths.map((width) => Math.min(this._options.maxWidth, width));
|
||||||
}
|
}
|
||||||
@@ -193,7 +192,7 @@ export class AutoColumnSize<T extends Slick.SlickData> implements Slick.Plugin<T
|
|||||||
let data = this._grid.getData() as Slick.DataProvider<T>;
|
let data = this._grid.getData() as Slick.DataProvider<T>;
|
||||||
let viewPort = this._grid.getViewport();
|
let viewPort = this._grid.getViewport();
|
||||||
let start = Math.max(0, viewPort.top);
|
let start = Math.max(0, viewPort.top);
|
||||||
let end = Math.min(data.getLength(), MAX_ROWS_TO_SCAN);
|
let end = Math.min(data.getLength(), viewPort.bottom);
|
||||||
for (let i = start; i < end; i++) {
|
for (let i = start; i < end; i++) {
|
||||||
texts.push(data.getItem(i)[columnDef.field!]);
|
texts.push(data.getItem(i)[columnDef.field!]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M1 1H14V14H1V1ZM13 13V8H2V13H13ZM13 7V2H2V7H13Z" fill="#323130"/>
|
<path d="M1 1H14V14H1V1ZM13 13V8H2V13H13ZM13 7V2H2V7H13Z" fill="#323130"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 178 B After Width: | Height: | Size: 155 B |
@@ -29,10 +29,11 @@ export const SearchViewFocusedKey = new RawContextKey<boolean>('notebookSearchVi
|
|||||||
export const InputBoxFocusedKey = new RawContextKey<boolean>('inputBoxFocus', false);
|
export const InputBoxFocusedKey = new RawContextKey<boolean>('inputBoxFocus', false);
|
||||||
export const SearchInputBoxFocusedKey = new RawContextKey<boolean>('searchInputBoxFocus', false);
|
export const SearchInputBoxFocusedKey = new RawContextKey<boolean>('searchInputBoxFocus', false);
|
||||||
|
|
||||||
|
// !! Do not change these or updates won't be able to deserialize editors correctly !!
|
||||||
export const UNTITLED_NOTEBOOK_TYPEID = 'workbench.editorinputs.untitledNotebookInput';
|
export const UNTITLED_NOTEBOOK_TYPEID = 'workbench.editorinputs.untitledNotebookInput';
|
||||||
export const UNTITLED_QUERY_EDITOR_TYPEID = 'workbench.editorinputs.untitledQueryInput';
|
export const UNTITLED_QUERY_EDITOR_TYPEID = 'workbench.editorInput.untitledQueryInput';
|
||||||
export const FILE_QUERY_EDITOR_TYPEID = 'workbench.editorinputs.fileQueryInput';
|
export const FILE_QUERY_EDITOR_TYPEID = 'workbench.editorInput.fileQueryInput';
|
||||||
export const RESOURCE_VIEWER_TYPEID = 'workbench.editorinputs.resourceViewerInput';
|
export const RESOURCE_VIEWER_TYPEID = 'workbench.editorInput.resourceViewerInput';
|
||||||
|
|
||||||
export const JUPYTER_PROVIDER_ID = 'jupyter';
|
export const JUPYTER_PROVIDER_ID = 'jupyter';
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,9 @@ export class SplitCellAction extends CellActionBase {
|
|||||||
this._register(context.cell.onCurrentEditModeChanged(currentMode => {
|
this._register(context.cell.onCurrentEditModeChanged(currentMode => {
|
||||||
this.enabled = currentMode === CellEditModes.WYSIWYG ? false : true;
|
this.enabled = currentMode === CellEditModes.WYSIWYG ? false : true;
|
||||||
}));
|
}));
|
||||||
|
this._register(context.cell.notebookModel.onCellTypeChanged(_ => {
|
||||||
|
this.enabled = context.cell.currentMode === CellEditModes.WYSIWYG ? false : true;
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,10 +59,6 @@ export class CellToolbarComponent {
|
|||||||
this._actionBar = new Taskbar(taskbar);
|
this._actionBar = new Taskbar(taskbar);
|
||||||
this._actionBar.context = context;
|
this._actionBar.context = context;
|
||||||
|
|
||||||
let splitCellButton = this.instantiationService.createInstance(SplitCellAction, 'notebook.SplitCellAtCursor', this.buttonSplitCell, 'masked-icon icon-split-cell');
|
|
||||||
splitCellButton.setListener(context);
|
|
||||||
splitCellButton.enabled = this.cellModel.cellType !== 'markdown';
|
|
||||||
|
|
||||||
let addCellsButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddCodeCell', localize('codeCellsPreview', "Add cell"), 'masked-pseudo code');
|
let addCellsButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddCodeCell', localize('codeCellsPreview', "Add cell"), 'masked-pseudo code');
|
||||||
|
|
||||||
let addCodeCellButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddCodeCell', localize('codePreview', "Code cell"), 'masked-pseudo code');
|
let addCodeCellButton = this.instantiationService.createInstance(AddCellAction, 'notebook.AddCodeCell', localize('codePreview', "Code cell"), 'masked-pseudo code');
|
||||||
@@ -74,6 +70,10 @@ export class CellToolbarComponent {
|
|||||||
let moveCellDownButton = this.instantiationService.createInstance(MoveCellAction, 'notebook.MoveCellDown', 'masked-icon move-down', this.buttonMoveDown);
|
let moveCellDownButton = this.instantiationService.createInstance(MoveCellAction, 'notebook.MoveCellDown', 'masked-icon move-down', this.buttonMoveDown);
|
||||||
let moveCellUpButton = this.instantiationService.createInstance(MoveCellAction, 'notebook.MoveCellUp', 'masked-icon move-up', this.buttonMoveUp);
|
let moveCellUpButton = this.instantiationService.createInstance(MoveCellAction, 'notebook.MoveCellUp', 'masked-icon move-up', this.buttonMoveUp);
|
||||||
|
|
||||||
|
let splitCellButton = this.instantiationService.createInstance(SplitCellAction, 'notebook.SplitCellAtCursor', this.buttonSplitCell, 'masked-icon icon-split-cell');
|
||||||
|
splitCellButton.setListener(context);
|
||||||
|
splitCellButton.enabled = this.cellModel.cellType !== 'markdown';
|
||||||
|
|
||||||
let deleteButton = this.instantiationService.createInstance(DeleteCellAction, 'notebook.DeleteCell', 'masked-icon delete', this.buttonDelete);
|
let deleteButton = this.instantiationService.createInstance(DeleteCellAction, 'notebook.DeleteCell', 'masked-icon delete', this.buttonDelete);
|
||||||
|
|
||||||
let moreActionsContainer = DOM.$('li.action-item');
|
let moreActionsContainer = DOM.$('li.action-item');
|
||||||
@@ -106,10 +106,10 @@ export class CellToolbarComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
taskbarContent.push(
|
taskbarContent.push(
|
||||||
{ action: splitCellButton },
|
|
||||||
{ element: addCellDropdownContainer },
|
{ element: addCellDropdownContainer },
|
||||||
{ action: moveCellDownButton },
|
{ action: moveCellDownButton },
|
||||||
{ action: moveCellUpButton },
|
{ action: moveCellUpButton },
|
||||||
|
{ action: splitCellButton },
|
||||||
{ action: deleteButton },
|
{ action: deleteButton },
|
||||||
{ element: moreActionsContainer });
|
{ element: moreActionsContainer });
|
||||||
|
|
||||||
|
|||||||
@@ -476,21 +476,28 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
|||||||
viewsDropdownMenuActionViewItem.setActionContext(this._notebookParams.notebookUri);
|
viewsDropdownMenuActionViewItem.setActionContext(this._notebookParams.notebookUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._actionBar.setContent([
|
|
||||||
{ element: buttonDropdownContainer },
|
|
||||||
{ action: this._runAllCellsAction },
|
|
||||||
{ element: Taskbar.createTaskbarSeparator() },
|
|
||||||
{ element: kernelContainer },
|
|
||||||
{ element: attachToContainer },
|
|
||||||
{ element: spacerElement },
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (this._showToolbarActions) {
|
if (this._showToolbarActions) {
|
||||||
this._actionBar.addElement(viewsDropdownContainer);
|
this._actionBar.setContent([
|
||||||
this._actionBar.addAction(collapseCellsAction);
|
{ element: buttonDropdownContainer },
|
||||||
this._actionBar.addAction(clearResultsButton);
|
{ action: this._runAllCellsAction },
|
||||||
this._actionBar.addAction(this._trustedAction);
|
{ element: Taskbar.createTaskbarSeparator() },
|
||||||
this._actionBar.addAction(runParametersAction);
|
{ element: kernelContainer },
|
||||||
|
{ element: attachToContainer },
|
||||||
|
{ element: spacerElement },
|
||||||
|
{ element: viewsDropdownContainer },
|
||||||
|
{ action: collapseCellsAction },
|
||||||
|
{ action: clearResultsButton },
|
||||||
|
{ action: this._trustedAction },
|
||||||
|
{ action: runParametersAction },
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
this._actionBar.setContent([
|
||||||
|
{ element: buttonDropdownContainer },
|
||||||
|
{ action: this._runAllCellsAction },
|
||||||
|
{ element: Taskbar.createTaskbarSeparator() },
|
||||||
|
{ element: kernelContainer },
|
||||||
|
{ element: attachToContainer },
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let kernelContainer = document.createElement('div');
|
let kernelContainer = document.createElement('div');
|
||||||
@@ -523,18 +530,25 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
|||||||
this._actionBar = new Taskbar(taskbar, { actionViewItemProvider: action => this.actionItemProvider(action as Action) });
|
this._actionBar = new Taskbar(taskbar, { actionViewItemProvider: action => this.actionItemProvider(action as Action) });
|
||||||
this._actionBar.context = this._notebookParams.notebookUri;
|
this._actionBar.context = this._notebookParams.notebookUri;
|
||||||
|
|
||||||
this._actionBar.setContent([
|
|
||||||
{ action: addCodeCellButton },
|
|
||||||
{ action: addTextCellButton },
|
|
||||||
{ element: kernelContainer },
|
|
||||||
{ element: attachToContainer },
|
|
||||||
{ action: this._runAllCellsAction },
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (this._showToolbarActions) {
|
if (this._showToolbarActions) {
|
||||||
this._actionBar.addAction(this._trustedAction);
|
this._actionBar.setContent([
|
||||||
this._actionBar.addAction(clearResultsButton);
|
{ action: addCodeCellButton },
|
||||||
this._actionBar.addAction(collapseCellsAction);
|
{ action: addTextCellButton },
|
||||||
|
{ element: kernelContainer },
|
||||||
|
{ element: attachToContainer },
|
||||||
|
{ action: this._trustedAction },
|
||||||
|
{ action: this._runAllCellsAction },
|
||||||
|
{ action: clearResultsButton },
|
||||||
|
{ action: collapseCellsAction },
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
this._actionBar.setContent([
|
||||||
|
{ action: addCodeCellButton },
|
||||||
|
{ action: addTextCellButton },
|
||||||
|
{ element: kernelContainer },
|
||||||
|
{ element: attachToContainer },
|
||||||
|
{ action: this._runAllCellsAction },
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export class NotebookEditorComponent extends AngularDisposable {
|
|||||||
public views: NotebookViewsExtension;
|
public views: NotebookViewsExtension;
|
||||||
public activeView: INotebookView;
|
public activeView: INotebookView;
|
||||||
public viewMode: ViewMode;
|
public viewMode: ViewMode;
|
||||||
public ViewMode = ViewMode;
|
public ViewMode = ViewMode; //For use of the enum in the template
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(ILogService) private readonly logService: ILogService,
|
@Inject(ILogService) private readonly logService: ILogService,
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ suite('NotebookViewModel', function (): void {
|
|||||||
viewModel.initialize();
|
viewModel.initialize();
|
||||||
|
|
||||||
let cell = viewModel.cells[0];
|
let cell = viewModel.cells[0];
|
||||||
let cellMeta = notebookViews.getCellMetadata(cell);
|
let cellMeta = notebookViews.getExtensionCellMetadata(cell);
|
||||||
|
|
||||||
assert(!isUndefinedOrNull(cellMeta.views.find(v => v.guid === viewModel.guid)));
|
assert(!isUndefinedOrNull(cellMeta.views.find(v => v.guid === viewModel.guid)));
|
||||||
assert.deepStrictEqual(viewModel.getCellMetadata(cell), cellMeta.views.find(v => v.guid === viewModel.guid));
|
assert.deepStrictEqual(viewModel.getCellMetadata(cell), cellMeta.views.find(v => v.guid === viewModel.guid));
|
||||||
|
|||||||
@@ -76,6 +76,19 @@ suite('NotebookViews', function (): void {
|
|||||||
notebookViews = await initializeExtension();
|
notebookViews = await initializeExtension();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should not modify the notebook document until a view is created', async () => {
|
||||||
|
//Create some content
|
||||||
|
notebookViews.notebook.addCell(CellTypes.Code, 0);
|
||||||
|
const cell = notebookViews.notebook.cells[0];
|
||||||
|
|
||||||
|
assert.strictEqual(notebookViews.getExtensionMetadata(), undefined);
|
||||||
|
assert.strictEqual(notebookViews.getExtensionCellMetadata(cell), undefined);
|
||||||
|
|
||||||
|
//Check that the view is created
|
||||||
|
notebookViews.createNewView(defaultViewName);
|
||||||
|
assert.notStrictEqual(notebookViews.getExtensionMetadata(), undefined);
|
||||||
|
});
|
||||||
|
|
||||||
test('create new view', async function (): Promise<void> {
|
test('create new view', async function (): Promise<void> {
|
||||||
assert.strictEqual(notebookViews.getViews().length, 0, 'notebook should not initially generate any views');
|
assert.strictEqual(notebookViews.getViews().length, 0, 'notebook should not initially generate any views');
|
||||||
|
|
||||||
|
|||||||
@@ -9,27 +9,36 @@ import { deepClone } from 'vs/base/common/objects';
|
|||||||
|
|
||||||
export class NotebookExtension<TNotebookMeta, TCellMeta> {
|
export class NotebookExtension<TNotebookMeta, TCellMeta> {
|
||||||
readonly version = 1;
|
readonly version = 1;
|
||||||
readonly extensionName = 'azuredatastudio';
|
|
||||||
readonly extensionNamespace = 'extensions';
|
readonly extensionNamespace = 'extensions';
|
||||||
|
|
||||||
public getNotebookMetadata(notebook: INotebookModel): TNotebookMeta {
|
private _extensionName: string;
|
||||||
|
|
||||||
|
public constructor(extensionName: string) {
|
||||||
|
this._extensionName = extensionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get extensionName(): string {
|
||||||
|
return this._extensionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getExtensionMetadata(notebook: INotebookModel): TNotebookMeta {
|
||||||
const metadata = notebook.getMetaValue(this.extensionNamespace) || {};
|
const metadata = notebook.getMetaValue(this.extensionNamespace) || {};
|
||||||
return metadata[this.extensionName] as TNotebookMeta;
|
return metadata[this.extensionName] as TNotebookMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setNotebookMetadata(notebook: INotebookModel, metadata: TNotebookMeta) {
|
public setExtensionMetadata(notebook: INotebookModel, metadata: TNotebookMeta) {
|
||||||
const meta = {};
|
const meta = {};
|
||||||
meta[this.extensionName] = metadata;
|
meta[this.extensionName] = metadata;
|
||||||
notebook.setMetaValue(this.extensionNamespace, meta);
|
notebook.setMetaValue(this.extensionNamespace, meta);
|
||||||
notebook.serializationStateChanged(NotebookChangeType.MetadataChanged);
|
notebook.serializationStateChanged(NotebookChangeType.MetadataChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCellMetadata(cell: ICellModel): TCellMeta {
|
public getExtensionCellMetadata(cell: ICellModel): TCellMeta {
|
||||||
const namespaceMeta = cell.metadata[this.extensionNamespace] || {};
|
const namespaceMeta = cell.metadata[this.extensionNamespace] || {};
|
||||||
return namespaceMeta[this.extensionName] as TCellMeta;
|
return namespaceMeta[this.extensionName] as TCellMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setCellMetadata(cell: ICellModel, metadata: TCellMeta) {
|
public setExtensionCellMetadata(cell: ICellModel, metadata: TCellMeta) {
|
||||||
const meta = {};
|
const meta = {};
|
||||||
meta[this.extensionName] = metadata;
|
meta[this.extensionName] = metadata;
|
||||||
cell.metadata[this.extensionNamespace] = meta;
|
cell.metadata[this.extensionNamespace] = meta;
|
||||||
|
|||||||
@@ -573,6 +573,11 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
|||||||
|
|
||||||
//Get selection value from current cell
|
//Get selection value from current cell
|
||||||
let newCellContent = model.getValueInRange(selection);
|
let newCellContent = model.getValueInRange(selection);
|
||||||
|
let startPosition = selection.getStartPosition();
|
||||||
|
//If the cursor is at the beginning of the cell with no selection, return
|
||||||
|
if (newCellContent.length === 0 && startPosition.lineNumber === 1 && startPosition.column === 1) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
//Get content after selection
|
//Get content after selection
|
||||||
let tailRange = range.setStartPosition(selection.endLineNumber, selection.endColumn);
|
let tailRange = range.setStartPosition(selection.endLineNumber, selection.endColumn);
|
||||||
@@ -631,6 +636,10 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
|||||||
partialSource = source.slice(tailRange.startLineNumber - 1, tailRange.startLineNumber)[0].slice(tailRange.startColumn - 1);
|
partialSource = source.slice(tailRange.startLineNumber - 1, tailRange.startLineNumber)[0].slice(tailRange.startColumn - 1);
|
||||||
tailSource.splice(0, 1, partialSource);
|
tailSource.splice(0, 1, partialSource);
|
||||||
}
|
}
|
||||||
|
//Remove the trailing empty line after the cursor
|
||||||
|
if (tailSource[0] === '\r\n' || tailSource[0] === '\n') {
|
||||||
|
tailSource.splice(0, 1);
|
||||||
|
}
|
||||||
tailCell.source = tailSource;
|
tailCell.source = tailSource;
|
||||||
tailCellIndex = newCellIndex + 1;
|
tailCellIndex = newCellIndex + 1;
|
||||||
this.insertCell(tailCell, tailCellIndex);
|
this.insertCell(tailCell, tailCellIndex);
|
||||||
|
|||||||
@@ -51,11 +51,11 @@ export class NotebookViewModel implements INotebookView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected initializeCell(cell: ICellModel, idx: number) {
|
protected initializeCell(cell: ICellModel, idx: number) {
|
||||||
let meta = this._notebookViews.getCellMetadata(cell);
|
let meta = this._notebookViews.getExtensionCellMetadata(cell);
|
||||||
|
|
||||||
if (!meta) {
|
if (!meta) {
|
||||||
this._notebookViews.initializeCell(cell);
|
this._notebookViews.initializeCell(cell);
|
||||||
meta = this._notebookViews.getCellMetadata(cell);
|
meta = this._notebookViews.getExtensionCellMetadata(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that we are not duplicting view entries in cell metadata
|
// Ensure that we are not duplicting view entries in cell metadata
|
||||||
@@ -91,7 +91,7 @@ export class NotebookViewModel implements INotebookView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getCellMetadata(cell: ICellModel): INotebookViewCell {
|
public getCellMetadata(cell: ICellModel): INotebookViewCell {
|
||||||
const meta = this._notebookViews.getCellMetadata(cell);
|
const meta = this._notebookViews.getExtensionCellMetadata(cell);
|
||||||
return meta?.views?.find(view => view.guid === this.guid);
|
return meta?.views?.find(view => view.guid === this.guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,30 +13,40 @@ import { INotebookView, INotebookViewCell, INotebookViewCellMetadata, INotebookV
|
|||||||
|
|
||||||
export class NotebookViewsExtension extends NotebookExtension<INotebookViewMetadata, INotebookViewCellMetadata> implements INotebookViews {
|
export class NotebookViewsExtension extends NotebookExtension<INotebookViewMetadata, INotebookViewCellMetadata> implements INotebookViews {
|
||||||
static readonly defaultViewName = localize('notebookView.untitledView', "Untitled View");
|
static readonly defaultViewName = localize('notebookView.untitledView', "Untitled View");
|
||||||
|
static readonly extension = 'notebookviews';
|
||||||
|
|
||||||
readonly maxNameIterationAttempts = 100;
|
readonly maxNameIterationAttempts = 100;
|
||||||
readonly extension = 'azuredatastudio';
|
|
||||||
override readonly version = 1;
|
override readonly version = 1;
|
||||||
|
|
||||||
protected _metadata: INotebookViewMetadata;
|
protected _metadata: INotebookViewMetadata | undefined;
|
||||||
|
private _initialized: boolean = false;
|
||||||
private _onViewDeleted = new Emitter<void>();
|
private _onViewDeleted = new Emitter<void>();
|
||||||
private _onActiveViewChanged = new Emitter<void>();
|
private _onActiveViewChanged = new Emitter<void>();
|
||||||
|
|
||||||
constructor(protected _notebook: INotebookModel) {
|
constructor(protected _notebook: INotebookModel) {
|
||||||
super();
|
super(NotebookViewsExtension.extension);
|
||||||
this.loadOrInitialize();
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadOrInitialize() {
|
public load(): void {
|
||||||
this._metadata = this.getNotebookMetadata(this._notebook);
|
this._metadata = this.getExtensionMetadata();
|
||||||
|
|
||||||
|
if (this._metadata) {
|
||||||
|
this._metadata.views = this._metadata.views.map(view => NotebookViewModel.load(view.guid, this));
|
||||||
|
this._initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public initialize() {
|
||||||
|
this._metadata = this.getExtensionMetadata();
|
||||||
|
|
||||||
if (!this._metadata) {
|
if (!this._metadata) {
|
||||||
this.initializeNotebook();
|
this.initializeNotebook();
|
||||||
this.initializeCells();
|
this.initializeCells();
|
||||||
this.commit();
|
this.commit();
|
||||||
} else {
|
|
||||||
this._metadata.views = this._metadata.views.map(view => NotebookViewModel.load(view.guid, this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected initializeNotebook() {
|
protected initializeNotebook() {
|
||||||
@@ -59,12 +69,17 @@ export class NotebookViewsExtension extends NotebookExtension<INotebookViewMetad
|
|||||||
views: []
|
views: []
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setCellMetadata(cell, meta);
|
this.setExtensionCellMetadata(cell, meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
public createNewView(name?: string): INotebookView {
|
public createNewView(name?: string): INotebookView {
|
||||||
const viewName = name || this.generateDefaultViewName();
|
const viewName = name || this.generateDefaultViewName();
|
||||||
|
|
||||||
|
// If the notebook has not been initialized, do it now
|
||||||
|
if (!this.initialized) {
|
||||||
|
this.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
const view = new NotebookViewModel(viewName, this);
|
const view = new NotebookViewModel(viewName, this);
|
||||||
view.initialize(true);
|
view.initialize(true);
|
||||||
|
|
||||||
@@ -77,21 +92,21 @@ export class NotebookViewsExtension extends NotebookExtension<INotebookViewMetad
|
|||||||
}
|
}
|
||||||
|
|
||||||
public removeView(guid: string) {
|
public removeView(guid: string) {
|
||||||
let viewToRemove = this._metadata.views.findIndex(view => view.guid === guid);
|
let viewToRemove = this._metadata?.views.findIndex(view => view.guid === guid);
|
||||||
if (viewToRemove !== -1) {
|
if (viewToRemove >= 0) {
|
||||||
let removedView = this._metadata.views.splice(viewToRemove, 1);
|
let removedView = this._metadata?.views.splice(viewToRemove, 1);
|
||||||
|
|
||||||
// Remove view data for each cell
|
// Remove view data for each cell
|
||||||
if (removedView.length === 1) {
|
if (removedView.length === 1) {
|
||||||
this._notebook?.cells.forEach((cell) => {
|
this._notebook?.cells.forEach((cell) => {
|
||||||
let meta = this.getCellMetadata(cell);
|
let meta = this.getExtensionCellMetadata(cell);
|
||||||
meta.views = meta.views.filter(x => x.guid !== removedView[0].guid);
|
meta.views = meta.views.filter(x => x.guid !== removedView[0].guid);
|
||||||
this.setCellMetadata(cell, meta);
|
this.setExtensionCellMetadata(cell, meta);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (guid === this._metadata.activeView) {
|
if (guid === this._metadata?.activeView) {
|
||||||
this._metadata.activeView = undefined;
|
this._metadata.activeView = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,13 +128,13 @@ export class NotebookViewsExtension extends NotebookExtension<INotebookViewMetad
|
|||||||
}
|
}
|
||||||
|
|
||||||
public updateCell(cell: ICellModel, currentView: INotebookView, cellData: INotebookViewCell, override: boolean = false) {
|
public updateCell(cell: ICellModel, currentView: INotebookView, cellData: INotebookViewCell, override: boolean = false) {
|
||||||
const cellMetadata = this.getCellMetadata(cell);
|
const cellMetadata = this.getExtensionCellMetadata(cell);
|
||||||
if (cellMetadata) {
|
if (cellMetadata) {
|
||||||
const viewToUpdate = cellMetadata.views.findIndex(view => view.guid === currentView.guid);
|
const viewToUpdate = cellMetadata.views.findIndex(view => view.guid === currentView.guid);
|
||||||
|
|
||||||
if (viewToUpdate >= 0) {
|
if (viewToUpdate >= 0) {
|
||||||
cellMetadata.views[viewToUpdate] = override ? cellData : { ...cellMetadata.views[viewToUpdate], ...cellData };
|
cellMetadata.views[viewToUpdate] = override ? cellData : { ...cellMetadata.views[viewToUpdate], ...cellData };
|
||||||
this.setCellMetadata(cell, cellMetadata);
|
this.setExtensionCellMetadata(cell, cellMetadata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,7 +144,7 @@ export class NotebookViewsExtension extends NotebookExtension<INotebookViewMetad
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getViews(): INotebookView[] {
|
public getViews(): INotebookView[] {
|
||||||
return this._metadata.views;
|
return this._metadata?.views ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public get metadata(): INotebookViewMetadata {
|
public get metadata(): INotebookViewMetadata {
|
||||||
@@ -137,21 +152,27 @@ export class NotebookViewsExtension extends NotebookExtension<INotebookViewMetad
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getCells(): INotebookViewCellMetadata[] {
|
public getCells(): INotebookViewCellMetadata[] {
|
||||||
return this._notebook.cells.map(cell => this.getCellMetadata(cell));
|
return this._notebook.cells.map(cell => this.getExtensionCellMetadata(cell));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override getExtensionMetadata(): INotebookViewMetadata {
|
||||||
|
return super.getExtensionMetadata(this._notebook);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getActiveView(): INotebookView {
|
public getActiveView(): INotebookView {
|
||||||
return this.getViews().find(view => view.guid === this._metadata.activeView);
|
return this.getViews().find(view => view.guid === this._metadata?.activeView);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setActiveView(view: INotebookView) {
|
public setActiveView(view: INotebookView) {
|
||||||
this._metadata.activeView = view.guid;
|
if (this._metadata) {
|
||||||
this._onActiveViewChanged.fire();
|
this._metadata.activeView = view.guid;
|
||||||
|
this._onActiveViewChanged.fire();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public commit() {
|
public commit() {
|
||||||
this._metadata = Object.assign({}, this._metadata);
|
this._metadata = Object.assign({}, this._metadata);
|
||||||
this.setNotebookMetadata(this._notebook, this._metadata);
|
this.setExtensionMetadata(this._notebook, this._metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
public viewNameIsTaken(name: string): boolean {
|
public viewNameIsTaken(name: string): boolean {
|
||||||
@@ -165,4 +186,8 @@ export class NotebookViewsExtension extends NotebookExtension<INotebookViewMetad
|
|||||||
public get onActiveViewChanged(): Event<void> {
|
public get onActiveViewChanged(): Event<void> {
|
||||||
return this._onActiveViewChanged.event;
|
return this._onActiveViewChanged.event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get initialized(): boolean {
|
||||||
|
return this._initialized;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -394,7 +394,7 @@ import 'vs/workbench/contrib/surveys/browser/languageSurveys.contribution';
|
|||||||
// Welcome
|
// Welcome
|
||||||
import 'vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay';
|
import 'vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay';
|
||||||
import 'vs/workbench/contrib/welcome/page/browser/welcomePage.contribution';
|
import 'vs/workbench/contrib/welcome/page/browser/welcomePage.contribution';
|
||||||
import 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution';
|
// import 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution'; // {{SQL CARBON EDIT}} - remove vscode getting started
|
||||||
import 'vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution';
|
import 'vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution';
|
||||||
|
|
||||||
// Call Hierarchy
|
// Call Hierarchy
|
||||||
|
|||||||
Reference in New Issue
Block a user