diff --git a/extensions/sql-database-projects/src/controllers/projectController.ts b/extensions/sql-database-projects/src/controllers/projectController.ts index da2882aa53..196c8703b7 100644 --- a/extensions/sql-database-projects/src/controllers/projectController.ts +++ b/extensions/sql-database-projects/src/controllers/projectController.ts @@ -36,7 +36,7 @@ import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/t import { IconPathHelper } from '../common/iconHelper'; import { DashboardData, PublishData, Status } from '../models/dashboardData/dashboardData'; import { getPublishDatabaseSettings, launchPublishTargetOption } from '../dialogs/publishDatabaseQuickpick'; -import { launchCreateAzureServerQuickPick, getPublishToDockerSettings } from '../dialogs/deployDatabaseQuickpick'; +import { launchCreateAzureServerQuickPick } from '../dialogs/deployDatabaseQuickpick'; import { DeployService } from '../models/deploy/deployService'; import { AddItemOptions, EntryType, GenerateProjectFromOpenApiSpecOptions, IDatabaseReferenceProjectEntry, ISqlProjectPublishSettings, IPublishToDockerSettings, ISqlProject, ItemType, SqlTargetPlatform } from 'sqldbproj'; import { AutorestHelper } from '../tools/autorestHelper'; @@ -47,6 +47,7 @@ import { FileProjectEntry, SqlProjectReferenceProjectEntry } from '../models/pro import { UpdateProjectAction, UpdateProjectDataModel } from '../models/api/updateProject'; import { AzureSqlClient } from '../models/deploy/azureSqlClient'; import { ConnectionService } from '../models/connections/connectionService'; +import { getPublishToDockerSettings } from '../dialogs/publishToDockerQuickpick'; const maxTableLength = 10; diff --git a/extensions/sql-database-projects/src/dialogs/deployDatabaseQuickpick.ts b/extensions/sql-database-projects/src/dialogs/deployDatabaseQuickpick.ts index 146d7d3892..52f59a94f5 100644 --- a/extensions/sql-database-projects/src/dialogs/deployDatabaseQuickpick.ts +++ b/extensions/sql-database-projects/src/dialogs/deployDatabaseQuickpick.ts @@ -7,14 +7,14 @@ import * as vscode from 'vscode'; import * as constants from '../common/constants'; import * as utils from '../common/utils'; import * as uiUtils from './utils'; -import { AppSettingType, DockerImageInfo, IDeployAppIntegrationProfile, ISqlDbDeployProfile } from '../models/deploy/deployProfile'; -import { Project } from '../models/project'; -import { getPublishDatabaseSettings } from './publishDatabaseQuickpick'; import * as path from 'path'; import * as fse from 'fs-extra'; +import { AppSettingType, IDeployAppIntegrationProfile, ISqlDbDeployProfile } from '../models/deploy/deployProfile'; +import { Project } from '../models/project'; +import { getPublishDatabaseSettings } from './publishDatabaseQuickpick'; import { AzureSqlClient } from '../models/deploy/azureSqlClient'; import { IAccount } from 'vscode-mssql'; -import { ISqlProjectPublishSettings, IDockerSettings, IPublishToDockerSettings, ISqlProject } from 'sqldbproj'; +import { ISqlProjectPublishSettings } from 'sqldbproj'; /** * Create flow for Deploying a database using only VS Code-native APIs such as QuickPick @@ -73,48 +73,6 @@ export async function launchDeployAppIntegrationQuickpick(project: Project): Pro }; } -async function launchEulaQuickPick(imageInfo: DockerImageInfo | undefined): Promise { - let eulaAccepted: boolean = false; - const agreementInfo = imageInfo?.agreementInfo; - if (agreementInfo) { - const openEulaButton: vscode.QuickInputButton = { - iconPath: new vscode.ThemeIcon('link-external'), - tooltip: constants.openEulaString - }; - const quickPick = vscode.window.createQuickPick(); - quickPick.items = [{ label: constants.yesString }, - { label: constants.noString }]; - quickPick.title = uiUtils.getAgreementDisplayText(agreementInfo); - quickPick.ignoreFocusOut = true; - quickPick.buttons = [openEulaButton]; - const disposables: vscode.Disposable[] = []; - try { - const eulaAcceptedPromise = new Promise((resolve) => { - disposables.push( - quickPick.onDidHide(() => { - resolve(false); - }), - quickPick.onDidTriggerButton(async () => { - await vscode.env.openExternal(vscode.Uri.parse(agreementInfo.link.url)); - }), - quickPick.onDidChangeSelection((item) => { - resolve(item[0].label === constants.yesString); - })); - }); - - quickPick.show(); - eulaAccepted = await eulaAcceptedPromise; - quickPick.hide(); - } - finally { - disposables.forEach(d => d.dispose()); - } - - return eulaAccepted; - } - return false; -} - export async function launchCreateAzureServerQuickPick(project: Project, azureSqlClient: AzureSqlClient): Promise { const name = uiUtils.getPublishServerName(project.getProjectTargetVersion()); @@ -271,129 +229,3 @@ export async function launchCreateAzureServerQuickPick(project: Project, azureSq } }; } - -/** - * Gets the settings for publishing a database to docker container using only VS Code-native APIs such as QuickPick - */ -export async function getPublishToDockerSettings(project: ISqlProject): Promise { - const target = project.getProjectTargetVersion(); - const name = uiUtils.getPublishServerName(target); - // Deploy to docker selected - let portNumber = await vscode.window.showInputBox({ - title: constants.enterPortNumber(name), - ignoreFocusOut: true, - value: constants.defaultPortNumber, - validateInput: input => !utils.validateSqlServerPortNumber(input) ? constants.portMustBeNumber : undefined - } - ); - - // Return when user hits escape - if (!portNumber) { - return undefined; - } - - let password: string | undefined = ''; - password = await vscode.window.showInputBox({ - title: constants.enterPassword(name), - ignoreFocusOut: true, - value: password, - validateInput: input => !utils.isValidSQLPassword(input) ? constants.invalidSQLPasswordMessage(name) : undefined, - password: true - } - ); - - // Return when user hits escape - if (!password) { - return undefined; - } - - let confirmPassword: string | undefined = ''; - confirmPassword = await vscode.window.showInputBox({ - title: constants.confirmPassword(name), - ignoreFocusOut: true, - value: confirmPassword, - validateInput: input => input !== password ? constants.passwordNotMatch(name) : undefined, - password: true - } - ); - - // Return when user hits escape - if (!confirmPassword) { - return undefined; - } - - const baseImages = uiUtils.getDockerBaseImages(target); - const baseImage = await vscode.window.showQuickPick( - baseImages.map(x => x.displayName), - { title: constants.selectBaseImage(name), ignoreFocusOut: true, placeHolder: uiUtils.getDockerImagePlaceHolder(target) }); - - // Return when user hits escape - if (!baseImage) { - return undefined; - } - - const imageInfo = baseImages.find(x => x.displayName === baseImage); - - if (!imageInfo) { - return undefined; - } - - const eulaAccepted = await launchEulaQuickPick(imageInfo); - if (!eulaAccepted) { - return undefined; - } - - let imageTags = await uiUtils.getImageTags(imageInfo, target); - let imageTagsItems: vscode.QuickPickItem[] = imageTags.map(tag => { return { label: tag }; }); - - if (imageInfo.defaultTag) { - // move the default to be the first one in the list - const defaultIndex = imageTagsItems.findIndex(i => i.label === imageInfo.defaultTag); - if (defaultIndex > -1) { - imageTagsItems.splice(defaultIndex, 1); - } - // add default next to the default value - imageTagsItems.unshift({ label: imageInfo.defaultTag, description: constants.defaultQuickPickItem }); - } - const imageTag = await vscode.window.showQuickPick( - imageTagsItems, - { title: constants.selectImageTag(name), ignoreFocusOut: true }); - - if (!imageTag) { - return undefined; - } - - // Add the image tag if it's not the latest - let imageName = imageInfo.name; - if (imageTag && imageTag.label !== constants.dockerImageDefaultTag) { - imageName = `${imageName}:${imageTag.label}`; - } - - const dockerSettings: IDockerSettings = { - serverName: constants.defaultLocalServerName, - userName: constants.defaultLocalServerAdminName, - dbName: project.projectFileName, - password: password, - port: +portNumber, - dockerBaseImage: imageName, - dockerBaseImageEula: imageInfo.agreementInfo.link.url - }; - - let deploySettings = await getPublishDatabaseSettings(project, false); - - // Return when user hits escape - if (!deploySettings) { - return undefined; - } - - // Server name should be set to localhost - deploySettings.serverName = dockerSettings.serverName; - - // Get the database name from deploy settings - dockerSettings.dbName = deploySettings.databaseName; - - return { - dockerSettings, - sqlProjectPublishSettings: deploySettings, - }; -} diff --git a/extensions/sql-database-projects/src/dialogs/publishToDockerQuickpick.ts b/extensions/sql-database-projects/src/dialogs/publishToDockerQuickpick.ts new file mode 100644 index 0000000000..5586caa0d8 --- /dev/null +++ b/extensions/sql-database-projects/src/dialogs/publishToDockerQuickpick.ts @@ -0,0 +1,179 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; +import * as constants from '../common/constants'; +import * as utils from '../common/utils'; +import * as uiUtils from './utils'; +import { IDockerSettings, IPublishToDockerSettings, ISqlProject } from 'sqldbproj'; +import { getPublishDatabaseSettings } from './publishDatabaseQuickpick'; +import { DockerImageInfo } from '../models/deploy/deployProfile'; + +/** + * Gets the settings for publishing a database to docker container using only VS Code-native APIs such as QuickPick + */ +export async function getPublishToDockerSettings(project: ISqlProject): Promise { + const target = project.getProjectTargetVersion(); + const name = uiUtils.getPublishServerName(target); + // Deploy to docker selected + let portNumber = await vscode.window.showInputBox({ + title: constants.enterPortNumber(name), + ignoreFocusOut: true, + value: constants.defaultPortNumber, + validateInput: input => !utils.validateSqlServerPortNumber(input) ? constants.portMustBeNumber : undefined + } + ); + + // Return when user hits escape + if (!portNumber) { + return undefined; + } + + let password: string | undefined = ''; + password = await vscode.window.showInputBox({ + title: constants.enterPassword(name), + ignoreFocusOut: true, + value: password, + validateInput: input => !utils.isValidSQLPassword(input) ? constants.invalidSQLPasswordMessage(name) : undefined, + password: true + } + ); + + // Return when user hits escape + if (!password) { + return undefined; + } + + let confirmPassword: string | undefined = ''; + confirmPassword = await vscode.window.showInputBox({ + title: constants.confirmPassword(name), + ignoreFocusOut: true, + value: confirmPassword, + validateInput: input => input !== password ? constants.passwordNotMatch(name) : undefined, + password: true + } + ); + + // Return when user hits escape + if (!confirmPassword) { + return undefined; + } + + const baseImages = uiUtils.getDockerBaseImages(target); + const baseImage = await vscode.window.showQuickPick( + baseImages.map(x => x.displayName), + { title: constants.selectBaseImage(name), ignoreFocusOut: true, placeHolder: uiUtils.getDockerImagePlaceHolder(target) }); + + // Return when user hits escape + if (!baseImage) { + return undefined; + } + + const imageInfo = baseImages.find(x => x.displayName === baseImage); + + if (!imageInfo) { + return undefined; + } + + const eulaAccepted = await launchEulaQuickPick(imageInfo); + if (!eulaAccepted) { + return undefined; + } + + let imageTags = await uiUtils.getImageTags(imageInfo, target); + let imageTagsItems: vscode.QuickPickItem[] = imageTags.map(tag => { return { label: tag }; }); + + if (imageInfo.defaultTag) { + // move the default to be the first one in the list + const defaultIndex = imageTagsItems.findIndex(i => i.label === imageInfo.defaultTag); + if (defaultIndex > -1) { + imageTagsItems.splice(defaultIndex, 1); + } + // add default next to the default value + imageTagsItems.unshift({ label: imageInfo.defaultTag, description: constants.defaultQuickPickItem }); + } + const imageTag = await vscode.window.showQuickPick( + imageTagsItems, + { title: constants.selectImageTag(name), ignoreFocusOut: true }); + + if (!imageTag) { + return undefined; + } + + // Add the image tag if it's not the latest + let imageName = imageInfo.name; + if (imageTag && imageTag.label !== constants.dockerImageDefaultTag) { + imageName = `${imageName}:${imageTag.label}`; + } + + const dockerSettings: IDockerSettings = { + serverName: constants.defaultLocalServerName, + userName: constants.defaultLocalServerAdminName, + dbName: project.projectFileName, + password: password, + port: +portNumber, + dockerBaseImage: imageName, + dockerBaseImageEula: imageInfo.agreementInfo.link.url + }; + + let deploySettings = await getPublishDatabaseSettings(project, false); + + // Return when user hits escape + if (!deploySettings) { + return undefined; + } + + // Server name should be set to localhost + deploySettings.serverName = dockerSettings.serverName; + + // Get the database name from deploy settings + dockerSettings.dbName = deploySettings.databaseName; + + return { + dockerSettings, + sqlProjectPublishSettings: deploySettings, + }; +} + +async function launchEulaQuickPick(imageInfo: DockerImageInfo | undefined): Promise { + let eulaAccepted: boolean = false; + const agreementInfo = imageInfo?.agreementInfo; + if (agreementInfo) { + const openEulaButton: vscode.QuickInputButton = { + iconPath: new vscode.ThemeIcon('link-external'), + tooltip: constants.openEulaString + }; + const quickPick = vscode.window.createQuickPick(); + quickPick.items = [{ label: constants.yesString }, + { label: constants.noString }]; + quickPick.title = uiUtils.getAgreementDisplayText(agreementInfo); + quickPick.ignoreFocusOut = true; + quickPick.buttons = [openEulaButton]; + const disposables: vscode.Disposable[] = []; + try { + const eulaAcceptedPromise = new Promise((resolve) => { + disposables.push( + quickPick.onDidHide(() => { + resolve(false); + }), + quickPick.onDidTriggerButton(async () => { + await vscode.env.openExternal(vscode.Uri.parse(agreementInfo.link.url)); + }), + quickPick.onDidChangeSelection((item) => { + resolve(item[0].label === constants.yesString); + })); + }); + + quickPick.show(); + eulaAccepted = await eulaAcceptedPromise; + quickPick.hide(); + } + finally { + disposables.forEach(d => d.dispose()); + } + + return eulaAccepted; + } + return false; +} diff --git a/extensions/sql-database-projects/src/projectProvider/projectProvider.ts b/extensions/sql-database-projects/src/projectProvider/projectProvider.ts index eb3f7e2da5..624aa5b1ba 100644 --- a/extensions/sql-database-projects/src/projectProvider/projectProvider.ts +++ b/extensions/sql-database-projects/src/projectProvider/projectProvider.ts @@ -14,7 +14,7 @@ import { SqlDatabaseProjectTreeViewProvider } from '../controllers/databaseProje import { ProjectsController } from '../controllers/projectController'; import { Project } from '../models/project'; import { BaseProjectTreeItem } from '../models/tree/baseTreeItem'; -import { getPublishToDockerSettings } from '../dialogs/deployDatabaseQuickpick'; +import { getPublishToDockerSettings } from '../dialogs/publishToDockerQuickpick'; import { getDockerImageSpec } from '../models/deploy/deployService'; export class SqlDatabaseProjectProvider implements dataworkspace.IProjectProvider, sqldbproj.IExtension {