diff --git a/extensions/sql-database-projects/package.json b/extensions/sql-database-projects/package.json index 14e75c2b87..fb41feb0c5 100644 --- a/extensions/sql-database-projects/package.json +++ b/extensions/sql-database-projects/package.json @@ -17,9 +17,7 @@ "onCommand:sqlDatabaseProjects.open", "onCommand:sqlDatabaseProjects.createProjectFromDatabase", "onCommand:sqlDatabaseProjects.updateProjectFromDatabase", - "onCommand:sqlDatabaseProjects.addSqlBinding", "onCommand:sqlDatabaseProjects.generateProjectFromOpenApiSpec", - "onCommand:sqlDatabaseProjects.addSqlBinding", "workspaceContains:**/*.sqlproj", "onView:dataworkspace.views.main" ], @@ -178,11 +176,6 @@ "title": "%sqlDatabaseProjects.editProjectFile%", "category": "%sqlDatabaseProjects.displayName%" }, - { - "command": "sqlDatabaseProjects.addSqlBinding", - "title": "%sqlDatabaseProjects.addSqlBinding%", - "category": "MS SQL" - }, { "command": "sqlDatabaseProjects.changeTargetPlatform", "title": "%sqlDatabaseProjects.changeTargetPlatform%", @@ -296,10 +289,6 @@ "command": "sqlDatabaseProjects.editProjectFile", "when": "false" }, - { - "command": "sqlDatabaseProjects.addSqlBinding", - "when": "editorLangId == csharp && !azdataAvailable && resourceScheme != untitled" - }, { "command": "sqlDatabaseProjects.exclude", "when": "false" @@ -461,7 +450,6 @@ "@microsoft/ads-extension-telemetry": "^1.1.5", "fast-glob": "^3.2.7", "fs-extra": "^5.0.0", - "jsonc-parser": "^2.3.1", "promisify-child-process": "^3.1.1", "semver": "^7.3.5", "vscode-languageclient": "^5.3.0-next.1", diff --git a/extensions/sql-database-projects/package.nls.json b/extensions/sql-database-projects/package.nls.json index 1091db9746..46cfd10152 100644 --- a/extensions/sql-database-projects/package.nls.json +++ b/extensions/sql-database-projects/package.nls.json @@ -38,7 +38,5 @@ "sqlDatabaseProjects.netCoreDoNotAsk": "Whether to prompt the user to install .NET Core 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.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.addSqlBinding":"Add SQL Binding (preview)" + "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)" } diff --git a/extensions/sql-database-projects/src/common/azureFunctionsUtils.ts b/extensions/sql-database-projects/src/common/azureFunctionsUtils.ts deleted file mode 100644 index 7bc19cc203..0000000000 --- a/extensions/sql-database-projects/src/common/azureFunctionsUtils.ts +++ /dev/null @@ -1,102 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as fse from 'fs-extra'; -import * as path from 'path'; -import * as vscode from 'vscode'; -import * as utils from './utils'; -import * as constants from './constants'; -import { parseJson } from './parseJson'; - -/** - * Represents the settings in an Azure function project's local.settings.json file - */ -export interface ILocalSettingsJson { - IsEncrypted?: boolean; - Values?: { [key: string]: string }; - Host?: { [key: string]: string }; - ConnectionStrings?: { [key: string]: string }; -} - -/** - * copied and modified from vscode-azurefunctions extension - * @param localSettingsPath full path to local.settings.json - * @returns settings in local.settings.json. If no settings are found, returns default "empty" settings - */ -export async function getLocalSettingsJson(localSettingsPath: string): Promise { - if (await fse.pathExists(localSettingsPath)) { - const data: string = (await fse.readFile(localSettingsPath)).toString(); - if (/[^\s]/.test(data)) { - try { - return parseJson(data); - } catch (error) { - throw new Error(constants.failedToParse(error.message)); - } - } - } - - return { - IsEncrypted: false // Include this by default otherwise the func cli assumes settings are encrypted and fails to run - }; -} - -/** - * Adds a new setting to a project's local.settings.json file - * modified from setLocalAppSetting code from vscode-azurefunctions extension - * @param projectFolder full path to project folder - * @param key Key of the new setting - * @param value Value of the new setting - * @returns true if successful adding the new setting, false if unsuccessful - */ -export async function setLocalAppSetting(projectFolder: string, key: string, value: string): Promise { - const localSettingsPath: string = path.join(projectFolder, constants.azureFunctionLocalSettingsFileName); - const settings: ILocalSettingsJson = await getLocalSettingsJson(localSettingsPath); - - settings.Values = settings.Values || {}; - if (settings.Values[key] === value) { - // don't do anything if it's the same as the existing value - return true; - } else if (settings.Values[key]) { - const result = await vscode.window.showWarningMessage(constants.settingAlreadyExists(key), { modal: true }, constants.yesString); - if (result !== constants.yesString) { - // key already exists and user doesn't want to overwrite it - return false; - } - } - - settings.Values[key] = value; - await fse.writeJson(localSettingsPath, settings, { spaces: 2 }); - - return true; -} - -/** - * Gets the Azure Functions project that contains the given file if the project is open in one of the workspace folders - * @param fileUri file that the containing project needs to be found for - * @returns uri of project or undefined if project couldn't be found - */ -export async function getAFProjectContainingFile(fileUri: vscode.Uri): Promise { - // get functions csprojs in the workspace - const projectPromises = vscode.workspace.workspaceFolders?.map(f => utils.getAllProjectsInFolder(f.uri, '.csproj')) ?? []; - const functionsProjects = (await Promise.all(projectPromises)).reduce((prev, curr) => prev.concat(curr), []).filter(p => isFunctionProject(path.dirname(p.fsPath))); - - // look for project folder containing file if there's more than one - if (functionsProjects.length > 1) { - // TODO: figure out which project contains the file - // the new style csproj doesn't list all the files in the project anymore, unless the file isn't in the same folder - // so we can't rely on using that to check - console.error('need to find which project contains the file ' + fileUri.fsPath); - return undefined; - } else if (functionsProjects.length === 0) { - throw new Error(constants.noAzureFunctionsProjectsInWorkspace); - } else { - return functionsProjects[0]; - } -} - -// Use 'host.json' as an indicator that this is a functions project -// copied from verifyIsproject.ts in vscode-azurefunctions extension -export async function isFunctionProject(folderPath: string): Promise { - return fse.pathExists(path.join(folderPath, constants.hostFileName)); -} diff --git a/extensions/sql-database-projects/src/common/constants.ts b/extensions/sql-database-projects/src/common/constants.ts index e7e735fe8f..8fdca5916f 100644 --- a/extensions/sql-database-projects/src/common/constants.ts +++ b/extensions/sql-database-projects/src/common/constants.ts @@ -533,48 +533,3 @@ export const defaultDSP = targetPlatformToVersion.get(defaultTargetPlatform)!; export function getTargetPlatformFromVersion(version: string): string { return Array.from(targetPlatformToVersion.keys()).filter(k => targetPlatformToVersion.get(k) === version)[0]; } - -// Insert SQL binding -export const hostFileName = 'host.json'; -export const sqlExtensionPackageName = 'Microsoft.Azure.WebJobs.Extensions.Sql'; -export const placeHolderObject = '[dbo].[table1]'; -export const sqlBindingsHelpLink = 'https://github.com/Azure/azure-functions-sql-extension/blob/main/README.md'; -export const passwordPlaceholder = '******'; - -export const input = localize('input', "Input"); -export const output = localize('output', "Output"); -export const selectBindingType = localize('selectBindingType', "Select type of binding"); -export const selectAzureFunction = localize('selectAzureFunction', "Select an Azure function in the current file to add SQL binding to"); -export const sqlTableOrViewToQuery = localize('sqlTableOrViewToQuery', "SQL table or view to query"); -export const sqlTableToUpsert = localize('sqlTableToUpsert', "SQL table to upsert into"); -export const connectionStringSetting = localize('connectionStringSetting', "Connection string setting name"); -export const selectSetting = localize('selectSetting', "Select SQL connection string setting from local.settings.json"); -export const connectionStringSettingPlaceholder = localize('connectionStringSettingPlaceholder', "Connection string setting specified in \"local.settings.json\""); -export const noAzureFunctionsInFile = localize('noAzureFunctionsInFile', "No Azure functions in the current active file"); -export const noAzureFunctionsProjectsInWorkspace = localize('noAzureFunctionsProjectsInWorkspace', "No Azure functions projects found in the workspace"); -export const addPackage = localize('addPackage', "Add Package"); -export const createNewLocalAppSetting = localize('createNewLocalAppSetting', 'Create new local app setting'); -export const createNewLocalAppSettingWithIcon = `$(add) ${createNewLocalAppSetting}`; -export const sqlConnectionStringSetting = 'SqlConnectionString'; -export const valueMustNotBeEmpty = localize('valueMustNotBeEmpty', "Value must not be empty"); -export const enterConnectionStringSettingName = localize('enterConnectionStringSettingName', "Enter connection string setting name"); -export const enterConnectionString = localize('enterConnectionString', "Enter connection string"); -export const saveChangesInFile = localize('saveChangesInFile', "There are unsaved changes in the current file. Save now?"); -export const save = localize('save', "Save"); -export function settingAlreadyExists(settingName: string) { return localize('SettingAlreadyExists', 'Local app setting \'{0}\' already exists. Overwrite?', settingName); } -export function failedToParse(errorMessage: string) { return localize('failedToParse', 'Failed to parse "{0}": {1}.', azureFunctionLocalSettingsFileName, errorMessage); } -export function jsonParseError(error: string, line: number, column: number) { return localize('jsonParseError', '{0} near line "{1}", column "{2}"', error, line, column); } -export const moreInformation = localize('moreInformation', "More Information"); -export const addPackageReferenceMessage = localize('addPackageReferenceMessage', 'To use SQL bindings, ensure your Azure Functions project has a reference to {0}', sqlExtensionPackageName); -export const addSqlBindingPackageError = localize('addSqlBindingPackageError', 'Error adding Sql Binding extension package to project'); -export const failedToGetConnectionString = localize('failedToGetConnectionString', 'An error occurred generating the connection string for the selected connection'); -export const connectionProfile = localize('connectionProfile', 'Select a connection profile'); -export const userConnectionString = localize('userConnectionString', 'Enter connection string'); -export const selectConnectionString = localize('selectConnectionString', 'Select SQL connection string method'); -export const selectConnectionError = (err?: any) => err ? localize('selectConnectionError', "Failed to set connection string app setting: {0}", utils.getErrorMessage(err)) : localize('unableToSetConnectionString', "Failed to set connection string app setting"); -export const includePassword = localize('includePassword', 'Do you want to include the password from this connection in your local.settings.json file?'); -export const enterPasswordPrompt = localize('enterPasswordPrompt', 'Enter the password to be used for the connection string'); -export const enterPasswordManually = localize('enterPasswordManually', 'Enter password or press escape to cancel'); -export const userPasswordLater = localize('userPasswordLater', 'In order to user the SQL connection string later you will need to manually enter the password in your local.settings.json file.'); -export const openFile = localize('openFile', "Open File"); -export const closeButton = localize('closeButton', "Close"); diff --git a/extensions/sql-database-projects/src/common/parseJson.ts b/extensions/sql-database-projects/src/common/parseJson.ts deleted file mode 100644 index de4c00db29..0000000000 --- a/extensions/sql-database-projects/src/common/parseJson.ts +++ /dev/null @@ -1,45 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -// copied from vscode-azurefunctions extension - -import * as jsonc from 'jsonc-parser'; -import * as constants from './constants'; - -/** - * Parses and returns JSON - * Has extra logic to remove a BOM character if it exists and handle comments - */ -export function parseJson(data: string): T { - if (data.charCodeAt(0) === 0xFEFF) { - data = data.slice(1); - } - - const errors: jsonc.ParseError[] = []; - const result: T = jsonc.parse(data, errors, { allowTrailingComma: true }); - if (errors.length > 0) { - const [line, column]: [number, number] = getLineAndColumnFromOffset(data, errors[0].offset); - throw new Error(constants.jsonParseError(jsonc.printParseErrorCode(errors[0].error), line, column)); - } else { - return result; - } -} - -export function getLineAndColumnFromOffset(data: string, offset: number): [number, number] { - const lines: string[] = data.split('\n'); - let charCount: number = 0; - let lineCount: number = 0; - let column: number = 0; - for (const line of lines) { - lineCount += 1; - const lineLength: number = line.length + 1; - charCount += lineLength; - if (charCount >= offset) { - column = offset - (charCount - lineLength); - break; - } - } - return [lineCount, column]; -} diff --git a/extensions/sql-database-projects/src/common/telemetry.ts b/extensions/sql-database-projects/src/common/telemetry.ts index edee7ea353..622df54001 100644 --- a/extensions/sql-database-projects/src/common/telemetry.ts +++ b/extensions/sql-database-projects/src/common/telemetry.ts @@ -16,7 +16,6 @@ export enum TelemetryViews { ProjectController = 'ProjectController', SqlProjectPublishDialog = 'SqlProjectPublishDialog', ProjectTree = 'ProjectTree', - SqlBindingsQuickPick = 'SqlBindingsQuickPick' } export enum TelemetryActions { @@ -33,7 +32,5 @@ export enum TelemetryActions { build = 'build', updateProjectForRoundtrip = 'updateProjectForRoundtrip', changePlatformType = 'changePlatformType', - updateSystemDatabaseReferencesInProjFile = 'updateSystemDatabaseReferencesInProjFile', - startAddSqlBinding = 'startAddSqlBinding', - finishAddSqlBinding = 'finishAddSqlBinding' + updateSystemDatabaseReferencesInProjFile = 'updateSystemDatabaseReferencesInProjFile' } diff --git a/extensions/sql-database-projects/src/common/utils.ts b/extensions/sql-database-projects/src/common/utils.ts index cbc7ff57f6..dd5854c823 100644 --- a/extensions/sql-database-projects/src/common/utils.ts +++ b/extensions/sql-database-projects/src/common/utils.ts @@ -302,16 +302,6 @@ export async function getSchemaCompareService(): Promise } } -export async function getAzureFunctionService(): Promise { - if (getAzdataApi()) { - // this isn't supported in ADS - throw new Error('Azure Functions service is not supported in Azure Data Studio'); - } else { - const api = await getVscodeMssqlApi(); - return api.azureFunctions; - } -} - export async function getVscodeMssqlApi(): Promise { const ext = vscode.extensions.getExtension(vscodeMssql.extension.name) as vscode.Extension; return ext.activate(); @@ -475,23 +465,6 @@ export async function detectCommandInstallation(command: string): Promise { - // path needs to use forward slashes for glob to work - const escapedPath = glob.escapePath(folder.fsPath.replace(/\\/g, '/')); - - // filter for projects with the specified project extension - const projFilter = path.posix.join(escapedPath, '**', `*${projectExtension}`); - - // glob will return an array of file paths with forward slashes, so they need to be converted back if on windows - return (await glob(projFilter)).map(p => vscode.Uri.file(path.resolve(p))); -} - export function validateSqlServerPortNumber(port: string | undefined): boolean { if (!port) { return false; diff --git a/extensions/sql-database-projects/src/controllers/mainController.ts b/extensions/sql-database-projects/src/controllers/mainController.ts index 51d1299dc6..a915369ad6 100644 --- a/extensions/sql-database-projects/src/controllers/mainController.ts +++ b/extensions/sql-database-projects/src/controllers/mainController.ts @@ -16,8 +16,6 @@ import { IconPathHelper } from '../common/iconHelper'; import { WorkspaceTreeItem } from 'dataworkspace'; import * as constants from '../common/constants'; import { SqlDatabaseProjectProvider } from '../projectProvider/projectProvider'; -import { launchAddSqlBindingQuickpick } from '../dialogs/addSqlBindingQuickpick'; -import { PackageHelper } from '../tools/packageHelper'; import { GenerateProjectFromOpenApiSpecOptions } from 'sqldbproj'; /** @@ -26,13 +24,11 @@ import { GenerateProjectFromOpenApiSpecOptions } from 'sqldbproj'; export default class MainController implements vscode.Disposable { protected projectsController: ProjectsController; protected netcoreTool: NetCoreTool; - protected packageHelper: PackageHelper; private _outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel(constants.projectsOutputChannel); public constructor(private context: vscode.ExtensionContext) { this.projectsController = new ProjectsController(this._outputChannel); this.netcoreTool = new NetCoreTool(this._outputChannel); - this.packageHelper = new PackageHelper(this._outputChannel); } public get extensionContext(): vscode.ExtensionContext { @@ -89,8 +85,6 @@ export default class MainController implements vscode.Disposable { vscode.commands.registerCommand('sqlDatabaseProjects.changeTargetPlatform', async (node: WorkspaceTreeItem) => { return this.projectsController.changeTargetPlatform(node); }); vscode.commands.registerCommand('sqlDatabaseProjects.validateExternalStreamingJob', async (node: WorkspaceTreeItem) => { return this.projectsController.validateExternalStreamingJob(node); }); - vscode.commands.registerCommand('sqlDatabaseProjects.addSqlBinding', async (uri: vscode.Uri | undefined) => { return launchAddSqlBindingQuickpick(uri, this.packageHelper); }); - IconPathHelper.setExtensionContext(this.extensionContext); await templates.loadTemplates(path.join(this.context.extensionPath, 'resources', 'templates')); diff --git a/extensions/sql-database-projects/src/dialogs/addSqlBindingQuickpick.ts b/extensions/sql-database-projects/src/dialogs/addSqlBindingQuickpick.ts deleted file mode 100644 index 7d70a2206c..0000000000 --- a/extensions/sql-database-projects/src/dialogs/addSqlBindingQuickpick.ts +++ /dev/null @@ -1,299 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 path from 'path'; -import { BindingType, ConnectionDetails, IConnectionInfo } from 'vscode-mssql'; -import * as constants from '../common/constants'; -import * as utils from '../common/utils'; -import * as azureFunctionsUtils from '../common/azureFunctionsUtils'; -import { PackageHelper } from '../tools/packageHelper'; -import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry'; - -export async function launchAddSqlBindingQuickpick(uri: vscode.Uri | undefined, packageHelper: PackageHelper): Promise { - TelemetryReporter.sendActionEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.startAddSqlBinding); - - const vscodeMssqlApi = await utils.getVscodeMssqlApi(); - - if (!uri) { - // this command only shows in the command palette when the active editor is a .cs file, so we can safely assume that's the scenario - // when this is called without a uri - uri = vscode.window.activeTextEditor!.document.uri; - - if (vscode.window.activeTextEditor!.document.isDirty) { - const result = await vscode.window.showWarningMessage(constants.saveChangesInFile, { modal: true }, constants.save); - - if (result !== constants.save) { - return; - } - - await vscode.window.activeTextEditor!.document.save(); - } - } - - // get all the Azure functions in the file - const azureFunctionsService = await utils.getAzureFunctionService(); - let getAzureFunctionsResult; - try { - getAzureFunctionsResult = await azureFunctionsService.getAzureFunctions(uri.fsPath); - } catch (e) { - void vscode.window.showErrorMessage(utils.getErrorMessage(e)); - return; - } - - const azureFunctions = getAzureFunctionsResult.azureFunctions; - - if (azureFunctions.length === 0) { - void vscode.window.showErrorMessage(constants.noAzureFunctionsInFile); - return; - } - - // 1. select Azure function from the current file - const azureFunctionName = (await vscode.window.showQuickPick(azureFunctions, { - canPickMany: false, - title: constants.selectAzureFunction, - ignoreFocusOut: true - })); - - if (!azureFunctionName) { - return; - } - - // 2. select input or output binding - const inputOutputItems: (vscode.QuickPickItem & { type: BindingType })[] = [ - { - label: constants.input, - type: BindingType.input - }, - { - label: constants.output, - type: BindingType.output - } - ]; - - const selectedBinding = (await vscode.window.showQuickPick(inputOutputItems, { - canPickMany: false, - title: constants.selectBindingType, - ignoreFocusOut: true - })); - - if (!selectedBinding) { - return; - } - - // 3. ask for object name for the binding - const objectName = await vscode.window.showInputBox({ - prompt: selectedBinding.type === BindingType.input ? constants.sqlTableOrViewToQuery : constants.sqlTableToUpsert, - placeHolder: constants.placeHolderObject, - validateInput: input => input ? undefined : constants.nameMustNotBeEmpty, - ignoreFocusOut: true - }); - - if (!objectName) { - return; - } - - // 4. ask for connection string setting name - let projectUri: vscode.Uri | undefined; - try { - projectUri = await azureFunctionsUtils.getAFProjectContainingFile(uri); - } catch (e) { - // continue even if there's no AF project found. The binding should still be able to be added as long as there was an azure function found in the file earlier - } - - let connectionStringSettingName; - - // show the settings from project's local.settings.json if there's an AF functions project - if (projectUri) { - let settings; - try { - settings = await azureFunctionsUtils.getLocalSettingsJson(path.join(path.dirname(projectUri.fsPath!), constants.azureFunctionLocalSettingsFileName)); - } catch (e) { - void vscode.window.showErrorMessage(utils.getErrorMessage(e)); - return; - } - - let existingSettings: (vscode.QuickPickItem)[] = []; - if (settings?.Values) { - existingSettings = Object.keys(settings.Values).map(setting => { - return { - label: setting - } as vscode.QuickPickItem; - }); - } - - existingSettings.unshift({ label: constants.createNewLocalAppSettingWithIcon }); - let sqlConnectionStringSettingExists = existingSettings.find(s => s.label === constants.sqlConnectionStringSetting); - - while (!connectionStringSettingName) { - const selectedSetting = await vscode.window.showQuickPick(existingSettings, { - canPickMany: false, - title: constants.selectSetting, - ignoreFocusOut: true - }); - if (!selectedSetting) { - // User cancelled - return; - } - - if (selectedSetting.label === constants.createNewLocalAppSettingWithIcon) { - const newConnectionStringSettingName = await vscode.window.showInputBox( - { - title: constants.enterConnectionStringSettingName, - ignoreFocusOut: true, - value: sqlConnectionStringSettingExists ? '' : constants.sqlConnectionStringSetting, - validateInput: input => input ? undefined : constants.nameMustNotBeEmpty - } - ) ?? ''; - - if (!newConnectionStringSettingName) { - // go back to select setting quickpick if user escapes from inputting the setting name in case they changed their mind - continue; - } - - // show the connection string methods (user input and connection profile options) - const listOfConnectionStringMethods = [constants.connectionProfile, constants.userConnectionString]; - while (true) { - const selectedConnectionStringMethod = await vscode.window.showQuickPick(listOfConnectionStringMethods, { - canPickMany: false, - title: constants.selectConnectionString, - ignoreFocusOut: true - }); - if (!selectedConnectionStringMethod) { - // User cancelled - return; - } - - let connectionString: string = ''; - let includePassword: string | undefined; - let connectionInfo: IConnectionInfo | undefined; - let connectionDetails: ConnectionDetails; - if (selectedConnectionStringMethod === constants.userConnectionString) { - // User chooses to enter connection string manually - connectionString = await vscode.window.showInputBox( - { - title: constants.enterConnectionString, - ignoreFocusOut: true, - value: 'Server=localhost;Initial Catalog={db_name};User ID=sa;Password={your_password};Persist Security Info=False', - validateInput: input => input ? undefined : constants.valueMustNotBeEmpty - } - ) ?? ''; - } else { - // Let user choose from existing connections to create connection string from - connectionInfo = await vscodeMssqlApi.promptForConnection(true); - if (!connectionInfo) { - // User cancelled return to selectedConnectionStringMethod prompt - continue; - } - connectionDetails = { options: connectionInfo }; - try { - // Prompt to include password in connection string if authentication type is SqlLogin and connection has password saved - if (connectionInfo.authenticationType === 'SqlLogin' && connectionInfo.password) { - includePassword = await vscode.window.showQuickPick([constants.yesString, constants.noString], { - title: constants.includePassword, - canPickMany: false, - ignoreFocusOut: true - }); - if (includePassword === constants.yesString) { - // set connection string to include password - connectionString = await vscodeMssqlApi.getConnectionString(connectionDetails, true, false); - } - } - // set connection string to not include the password if connection info does not include password, or user chooses to not include password, or authentication type is not sql login - if (includePassword !== constants.yesString) { - connectionString = await vscodeMssqlApi.getConnectionString(connectionDetails, false, false); - } - } catch (e) { - // failed to get connection string for selected connection and will go back to prompt for connection string methods - console.warn(e); - void vscode.window.showErrorMessage(constants.failedToGetConnectionString); - continue; - } - } - if (connectionString) { - try { - const projectFolder: string = path.dirname(projectUri.fsPath); - const localSettingsPath: string = path.join(projectFolder, constants.azureFunctionLocalSettingsFileName); - let userPassword: string | undefined; - // Ask user to enter password if auth type is sql login and password is not saved - if (connectionInfo?.authenticationType === 'SqlLogin' && !connectionInfo?.password) { - userPassword = await vscode.window.showInputBox({ - prompt: constants.enterPasswordPrompt, - placeHolder: constants.enterPasswordManually, - ignoreFocusOut: true, - password: true, - validateInput: input => input ? undefined : constants.valueMustNotBeEmpty - }); - if (userPassword) { - // if user enters password replace password placeholder with user entered password - connectionString = connectionString.replace(constants.passwordPlaceholder, userPassword); - } - } - if (includePassword !== constants.yesString && !userPassword && connectionInfo?.authenticationType === 'SqlLogin') { - // if user does not want to include password or user does not enter password, show warning message that they will have to enter it manually later in local.settings.json - void vscode.window.showWarningMessage(constants.userPasswordLater, constants.openFile, constants.closeButton).then(async (result) => { - if (result === constants.openFile) { - // open local.settings.json file - void vscode.commands.executeCommand(constants.vscodeOpenCommand, vscode.Uri.file(localSettingsPath)); - } - }); - } - const success = await azureFunctionsUtils.setLocalAppSetting(projectFolder, newConnectionStringSettingName, connectionString); - if (success) { - // exit both loops and insert binding - connectionStringSettingName = newConnectionStringSettingName; - break; - } else { - void vscode.window.showErrorMessage(constants.selectConnectionError()); - } - } catch (e) { - // display error message and show select setting quickpick again - void vscode.window.showErrorMessage(constants.selectConnectionError(e)); - continue; - } - } - } - } else { - // If user cancels out of this or doesn't want to overwrite an existing setting - // just return them to the select setting quickpick in case they changed their mind - connectionStringSettingName = selectedSetting.label; - } - } - } else { - // if no AF project was found or there's more than one AF functions project in the workspace, - // ask for the user to input the setting name - connectionStringSettingName = await vscode.window.showInputBox({ - prompt: constants.connectionStringSetting, - placeHolder: constants.connectionStringSettingPlaceholder, - ignoreFocusOut: true - }); - } - - if (!connectionStringSettingName) { - return; - } - - // 5. insert binding - try { - const result = await azureFunctionsService.addSqlBinding(selectedBinding.type, uri.fsPath, azureFunctionName, objectName, connectionStringSettingName); - - if (!result.success) { - void vscode.window.showErrorMessage(result.errorMessage); - TelemetryReporter.sendErrorEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.finishAddSqlBinding); - return; - } - - TelemetryReporter.createActionEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.finishAddSqlBinding) - .withAdditionalProperties({ bindingType: selectedBinding.label }) - .send(); - } catch (e) { - void vscode.window.showErrorMessage(utils.getErrorMessage(e)); - TelemetryReporter.sendErrorEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.finishAddSqlBinding); - return; - } - - // 6. Add sql extension package reference to project. If the reference is already there, it doesn't get added again - await packageHelper.addPackageToAFProjectContainingFile(uri, constants.sqlExtensionPackageName); -} diff --git a/extensions/sql-database-projects/src/test/dialogs/addSqlBindingQuickpick.test.ts b/extensions/sql-database-projects/src/test/dialogs/addSqlBindingQuickpick.test.ts deleted file mode 100644 index f9a2cb41a8..0000000000 --- a/extensions/sql-database-projects/src/test/dialogs/addSqlBindingQuickpick.test.ts +++ /dev/null @@ -1,134 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as should from 'should'; -import * as sinon from 'sinon'; -import * as vscode from 'vscode'; -import * as TypeMoq from 'typemoq'; -import * as utils from '../../common/utils'; -import * as constants from '../../common/constants'; -import * as azureFunctionUtils from '../../common/azureFunctionsUtils'; - -import { createContext, TestContext, createTestCredentials } from '../testContext'; -import { launchAddSqlBindingQuickpick } from '../../dialogs/addSqlBindingQuickpick'; -import { PackageHelper } from '../../tools/packageHelper'; - -let testContext: TestContext; -let packageHelper: PackageHelper; -describe('Add SQL Binding quick pick', () => { - beforeEach(function (): void { - testContext = createContext(); - packageHelper = new PackageHelper(testContext.outputChannel); - }); - - afterEach(function (): void { - sinon.restore(); - }); - - it('Should show error if the file contains no Azure Functions', async function (): Promise { - sinon.stub(utils, 'getAzureFunctionService').resolves(testContext.azureFunctionService.object); - sinon.stub(utils, 'getVscodeMssqlApi').resolves(testContext.vscodeMssqlIExtension.object); - const spy = sinon.spy(vscode.window, 'showErrorMessage'); - testContext.azureFunctionService.setup(x => x.getAzureFunctions(TypeMoq.It.isAny())).returns(async () => { - return Promise.resolve({ - success: true, - errorMessage: '', - azureFunctions: [] - }); - }); - await launchAddSqlBindingQuickpick(vscode.Uri.file('testUri'), packageHelper); - - const msg = constants.noAzureFunctionsInFile; - should(spy.calledOnce).be.true('showErrorMessage should have been called exactly once'); - should(spy.calledWith(msg)).be.true(`showErrorMessage not called with expected message '${msg}' Actual '${spy.getCall(0).args[0]}'`); - }); - - it('Should show error if adding SQL binding was not successful', async function (): Promise { - sinon.stub(utils, 'getAzureFunctionService').resolves(testContext.azureFunctionService.object); - sinon.stub(utils, 'getVscodeMssqlApi').resolves(testContext.vscodeMssqlIExtension.object); - const spy = sinon.spy(vscode.window, 'showErrorMessage'); - testContext.azureFunctionService.setup(x => x.getAzureFunctions(TypeMoq.It.isAny())).returns(async () => { - return Promise.resolve({ - success: true, - errorMessage: '', - azureFunctions: ['af1', 'af2'] - }); - }); - //failure since no AFs are found in the project - sinon.stub(azureFunctionUtils, 'getAFProjectContainingFile').resolves(undefined); - const errormsg = 'Error inserting binding'; - testContext.azureFunctionService.setup(x => x.addSqlBinding(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(async () => { - return Promise.resolve({ - success: false, - errorMessage: errormsg - }); - }); - - // select Azure function - let quickpickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves({ label: 'af1' }); - // select input or output binding - quickpickStub.onSecondCall().resolves({ label: constants.input }); - // give object name - let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('dbo.table1'); - // give connection string setting name - inputBoxStub.onSecondCall().resolves('sqlConnectionString'); - - await launchAddSqlBindingQuickpick(vscode.Uri.file('testUri'), packageHelper); - - should(spy.calledOnce).be.true('showErrorMessage should have been called exactly once'); - should(spy.calledWith(errormsg)).be.true(`showErrorMessage not called with expected message '${errormsg}' Actual '${spy.getCall(0).args[0]}'`); - }); - - it('Should show error connection profile does not connect', async function (): Promise { - sinon.stub(utils, 'getAzureFunctionService').resolves(testContext.azureFunctionService.object); - sinon.stub(utils, 'getVscodeMssqlApi').resolves(testContext.vscodeMssqlIExtension.object); - let connectionCreds = createTestCredentials(); - - sinon.stub(azureFunctionUtils, 'getAFProjectContainingFile').resolves(vscode.Uri.file('testUri')); - testContext.azureFunctionService.setup(x => x.getAzureFunctions(TypeMoq.It.isAny())).returns(async () => { - return Promise.resolve({ - success: true, - errorMessage: '', - azureFunctions: ['af1'] - }); - }); - - // Mocks connect call to mssql - let error = new Error('Connection Request Failed'); - testContext.vscodeMssqlIExtension.setup(x => x.connect(TypeMoq.It.isAny(), undefined)).throws(error); - - // Mocks promptForConnection - testContext.vscodeMssqlIExtension.setup(x => x.promptForConnection(true)).returns(() => Promise.resolve(connectionCreds)); - let quickpickStub = sinon.stub(vscode.window, 'showQuickPick'); - // select Azure function - quickpickStub.onFirstCall().resolves({ label: 'af1' }); - // select input or output binding - quickpickStub.onSecondCall().resolves({ label: constants.input }); - - // give object name - let inputBoxStub = sinon.stub(vscode.window, 'showInputBox').onFirstCall().resolves('dbo.table1'); - - // select connection profile - quickpickStub.onThirdCall().resolves({ label: constants.createNewLocalAppSettingWithIcon }); - - // give connection string setting name - inputBoxStub.onSecondCall().resolves('SqlConnectionString'); - - // select connection profile method - quickpickStub.onCall(3).resolves({ label: constants.connectionProfile }); - - await launchAddSqlBindingQuickpick(vscode.Uri.file('testUri'), packageHelper); - - // should go back to the select connection string methods - should(quickpickStub.callCount === 5); - should(quickpickStub.getCall(4).args).deepEqual([ - [constants.connectionProfile, constants.userConnectionString], - { - canPickMany: false, - ignoreFocusOut: true, - title: constants.selectConnectionString - }]); - }); -}); diff --git a/extensions/sql-database-projects/src/test/packageHelper.test.ts b/extensions/sql-database-projects/src/test/packageHelper.test.ts deleted file mode 100644 index 2e32bb82a9..0000000000 --- a/extensions/sql-database-projects/src/test/packageHelper.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - - -import * as should from 'should'; -import * as os from 'os'; -import * as vscode from 'vscode'; -import * as sinon from 'sinon'; -import * as constants from '../common/constants'; -import * as azureFunctionUtils from '../common/azureFunctionsUtils'; -import { PackageHelper } from '../tools/packageHelper'; -import { createContext, TestContext } from './testContext'; - -let testContext: TestContext; -let packageHelper: PackageHelper; - -describe('PackageHelper tests', function (): void { - beforeEach(function (): void { - testContext = createContext(); - packageHelper = new PackageHelper(testContext.outputChannel); - }); - - afterEach(function (): void { - sinon.restore(); - }); - - it('Should construct correct add Package Arguments', function (): void { - const packageHelper = new PackageHelper( vscode.window.createOutputChannel('db project test')); - const projectUri = vscode.Uri.file('dummy\\project\\path.csproj'); - const result = packageHelper.constructAddPackageArguments(projectUri, constants.sqlExtensionPackageName); - - if (os.platform() === 'win32') { - should(result).equal(` add "\\\\dummy\\\\project\\\\path.csproj" package ${constants.sqlExtensionPackageName} --prerelease`); - } - else { - should(result).equal(` add "/dummy/project/path.csproj" package ${constants.sqlExtensionPackageName} --prerelease`); - } - }); - - it('Should construct correct add Package Arguments with version', function (): void { - const packageHelper = new PackageHelper( vscode.window.createOutputChannel('db project test')); - const projectUri = vscode.Uri.file('dummy\\project\\path.csproj'); - const result = packageHelper.constructAddPackageArguments(projectUri, constants.sqlExtensionPackageName, constants.VersionNumber); - - if (os.platform() === 'win32') { - should(result).equal(` add "\\\\dummy\\\\project\\\\path.csproj" package ${constants.sqlExtensionPackageName} -v ${constants.VersionNumber}`); - } - else { - should(result).equal(` add "/dummy/project/path.csproj" package ${constants.sqlExtensionPackageName} -v ${constants.VersionNumber}`); - } - }); - - it('Should show info message to add sql bindings package if project is not found', async function (): Promise { - sinon.stub(azureFunctionUtils, 'getAFProjectContainingFile').resolves(undefined); - const spy = sinon.spy(vscode.window, 'showInformationMessage'); - - await packageHelper.addPackageToAFProjectContainingFile(vscode.Uri.file(''), constants.sqlExtensionPackageName); - should(spy.calledOnce).be.true('showInformationMessage should have been called exactly once'); - }); -}); diff --git a/extensions/sql-database-projects/src/test/testContext.ts b/extensions/sql-database-projects/src/test/testContext.ts index ea634c77b2..f75347d355 100644 --- a/extensions/sql-database-projects/src/test/testContext.ts +++ b/extensions/sql-database-projects/src/test/testContext.ts @@ -8,16 +8,11 @@ import * as azdata from 'azdata'; import * as path from 'path'; import * as TypeMoq from 'typemoq'; import * as mssql from '../../../mssql/src/mssql'; -import * as vscodeMssql from 'vscode-mssql'; export interface TestContext { context: vscode.ExtensionContext; dacFxService: TypeMoq.IMock; - azureFunctionService: TypeMoq.IMock; outputChannel: vscode.OutputChannel; - vscodeMssqlIExtension: TypeMoq.IMock - dacFxMssqlService: TypeMoq.IMock; - schemaCompareService: TypeMoq.IMock; } export const mockDacFxResult = { @@ -124,153 +119,6 @@ export class MockDacFxService implements mssql.IDacFxService { public validateStreamingJob(_: string, __: string): Thenable { return Promise.resolve(mockDacFxResult); } } -export const mockResultStatus = { - success: true, - errorMessage: '' -}; - -export const mockGetAzureFunctionsResult = { - success: true, - errorMessage: '', - azureFunctions: [] -}; - -export class MockAzureFunctionService implements vscodeMssql.IAzureFunctionsService { - addSqlBinding(_: vscodeMssql.BindingType, __: string, ___: string, ____: string, _____: string): Thenable { return Promise.resolve(mockResultStatus); } - getAzureFunctions(_: string): Thenable { return Promise.resolve(mockGetAzureFunctionsResult); } -} - -export const mockDacFxMssqlOptionResult: vscodeMssql.DacFxOptionsResult = { - success: true, - errorMessage: '', - deploymentOptions: { - ignoreTableOptions: false, - ignoreSemicolonBetweenStatements: false, - ignoreRouteLifetime: false, - ignoreRoleMembership: false, - ignoreQuotedIdentifiers: false, - ignorePermissions: false, - ignorePartitionSchemes: false, - ignoreObjectPlacementOnPartitionScheme: false, - ignoreNotForReplication: false, - ignoreLoginSids: false, - ignoreLockHintsOnIndexes: false, - ignoreKeywordCasing: false, - ignoreIndexPadding: false, - ignoreIndexOptions: false, - ignoreIncrement: false, - ignoreIdentitySeed: false, - ignoreUserSettingsObjects: false, - ignoreFullTextCatalogFilePath: false, - ignoreWhitespace: false, - ignoreWithNocheckOnForeignKeys: false, - verifyCollationCompatibility: false, - unmodifiableObjectWarnings: false, - treatVerificationErrorsAsWarnings: false, - scriptRefreshModule: false, - scriptNewConstraintValidation: false, - scriptFileSize: false, - scriptDeployStateChecks: false, - scriptDatabaseOptions: false, - scriptDatabaseCompatibility: false, - scriptDatabaseCollation: false, - runDeploymentPlanExecutors: false, - registerDataTierApplication: false, - populateFilesOnFileGroups: false, - noAlterStatementsToChangeClrTypes: false, - includeTransactionalScripts: false, - includeCompositeObjects: false, - allowUnsafeRowLevelSecurityDataMovement: false, - ignoreWithNocheckOnCheckConstraints: false, - ignoreFillFactor: false, - ignoreFileSize: false, - ignoreFilegroupPlacement: false, - doNotAlterReplicatedObjects: false, - doNotAlterChangeDataCaptureObjects: false, - disableAndReenableDdlTriggers: false, - deployDatabaseInSingleUserMode: false, - createNewDatabase: false, - compareUsingTargetCollation: false, - commentOutSetVarDeclarations: false, - blockWhenDriftDetected: false, - blockOnPossibleDataLoss: false, - backupDatabaseBeforeChanges: false, - allowIncompatiblePlatform: false, - allowDropBlockingAssemblies: false, - dropConstraintsNotInSource: false, - dropDmlTriggersNotInSource: false, - dropExtendedPropertiesNotInSource: false, - dropIndexesNotInSource: false, - ignoreFileAndLogFilePath: false, - ignoreExtendedProperties: false, - ignoreDmlTriggerState: false, - ignoreDmlTriggerOrder: false, - ignoreDefaultSchema: false, - ignoreDdlTriggerState: false, - ignoreDdlTriggerOrder: false, - ignoreCryptographicProviderFilePath: false, - verifyDeployment: false, - ignoreComments: false, - ignoreColumnCollation: false, - ignoreAuthorizer: false, - ignoreAnsiNulls: false, - generateSmartDefaults: false, - dropStatisticsNotInSource: false, - dropRoleMembersNotInSource: false, - dropPermissionsNotInSource: false, - dropObjectsNotInSource: false, - ignoreColumnOrder: false, - doNotDropObjectTypes: [], - excludeObjectTypes: [] - } -}; - -export class MockDacFxMssqlService implements vscodeMssql.IDacFxService { - public exportBacpac(_: string, __: string, ___: string, ____: vscodeMssql.TaskExecutionMode): Thenable { return Promise.resolve(mockDacFxResult); } - public importBacpac(_: string, __: string, ___: string, ____: vscodeMssql.TaskExecutionMode): Thenable { return Promise.resolve(mockDacFxResult); } - public extractDacpac(_: string, __: string, ___: string, ____: string, _____: string, ______: vscodeMssql.TaskExecutionMode): Thenable { return Promise.resolve(mockDacFxResult); } - public createProjectFromDatabase(_: string, __: string, ___: string, ____: string, _____: string, ______: vscodeMssql.ExtractTarget, _______: vscodeMssql.TaskExecutionMode): Thenable { return Promise.resolve(mockDacFxResult); } - public deployDacpac(_: string, __: string, ___: boolean, ____: string, _____: vscodeMssql.TaskExecutionMode, ______?: Record): Thenable { return Promise.resolve(mockDacFxResult); } - public generateDeployScript(_: string, __: string, ___: string, ____: vscodeMssql.TaskExecutionMode, ______?: Record): Thenable { return Promise.resolve(mockDacFxResult); } - public generateDeployPlan(_: string, __: string, ___: string, ____: vscodeMssql.TaskExecutionMode): Thenable { return Promise.resolve(mockDacFxResult); } - public getOptionsFromProfile(_: string): Thenable { return Promise.resolve(mockDacFxMssqlOptionResult); } - public validateStreamingJob(_: string, __: string): Thenable { return Promise.resolve(mockDacFxResult); } -} - -export class MockSchemaCompareService implements vscodeMssql.ISchemaCompareService { - schemaCompareGetDefaultOptions(): Thenable { - throw new Error('Method not implemented.'); - } -} - -export class MockVscodeMssqlIExtension implements vscodeMssql.IExtension { - sqlToolsServicePath: string = ''; - dacFx: vscodeMssql.IDacFxService; - schemaCompare: vscodeMssql.ISchemaCompareService; - azureFunctions: vscodeMssql.IAzureFunctionsService; - - constructor() { - this.dacFx = new MockDacFxMssqlService; - this.schemaCompare = new MockSchemaCompareService; - this.azureFunctions = new MockAzureFunctionService; - } - promptForConnection(_?: boolean): Promise { - throw new Error('Method not implemented.'); - } - connect(_: vscodeMssql.IConnectionInfo, __?: boolean): Promise { - throw new Error('Method not implemented.'); - } - listDatabases(_: string): Promise { - throw new Error('Method not implemented.'); - } - getDatabaseNameFromTreeNode(_: vscodeMssql.ITreeNodeInfo): string { - throw new Error('Method not implemented.'); - } - getConnectionString(_: string | vscodeMssql.ConnectionDetails, ___?: boolean, _____?: boolean): Promise { - throw new Error('Method not implemented.'); - } -} - export function createContext(): TestContext { let extensionPath = path.join(__dirname, '..', '..'); @@ -303,10 +151,6 @@ export function createContext(): TestContext { extension: undefined as any }, dacFxService: TypeMoq.Mock.ofType(MockDacFxService), - azureFunctionService: TypeMoq.Mock.ofType(MockAzureFunctionService), - vscodeMssqlIExtension: TypeMoq.Mock.ofType(MockVscodeMssqlIExtension), - dacFxMssqlService: TypeMoq.Mock.ofType(MockDacFxMssqlService), - schemaCompareService: TypeMoq.Mock.ofType(MockSchemaCompareService), outputChannel: { name: '', append: () => { }, @@ -342,41 +186,3 @@ export const mockConnectionProfile: azdata.IConnectionProfile = { connectionName: 'My Connection Name' } }; - -export function createTestCredentials(): vscodeMssql.IConnectionInfo { - const creds: vscodeMssql.IConnectionInfo = { - server: 'my-server', - database: 'my_db', - user: 'sa', - password: '12345678', - email: 'test-email', - accountId: 'test-account-id', - port: 1234, - authenticationType: 'test', - azureAccountToken: '', - expiresOn: 0, - encrypt: false, - trustServerCertificate: false, - persistSecurityInfo: false, - connectTimeout: 15, - connectRetryCount: 0, - connectRetryInterval: 0, - applicationName: 'vscode-mssql', - workstationId: 'test', - applicationIntent: '', - currentLanguage: '', - pooling: true, - maxPoolSize: 15, - minPoolSize: 0, - loadBalanceTimeout: 0, - replication: false, - attachDbFilename: '', - failoverPartner: '', - multiSubnetFailover: false, - multipleActiveResultSets: false, - packetSize: 8192, - typeSystemVersion: 'Latest', - connectionString: '' - }; - return creds; -} diff --git a/extensions/sql-database-projects/src/tools/packageHelper.ts b/extensions/sql-database-projects/src/tools/packageHelper.ts deleted file mode 100644 index 656e35e8ac..0000000000 --- a/extensions/sql-database-projects/src/tools/packageHelper.ts +++ /dev/null @@ -1,84 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 utils from '../common/utils'; -import * as azureFunctionsUtils from '../common/azureFunctionsUtils'; -import * as constants from '../common/constants'; -import { NetCoreTool } from './netcoreTool'; -import { ShellCommandOptions } from './shellExecutionHelper'; - -export class PackageHelper { - private netCoreTool: NetCoreTool; - private _outputChannel: vscode.OutputChannel; - - constructor(outputChannel: vscode.OutputChannel) { - this.netCoreTool = new NetCoreTool(outputChannel); - this._outputChannel = outputChannel; - } - - /** - * Constructs the parameters for a dotnet add package - * @param projectUri uri of project to add package to - * @param packageName name of package - * @param packageVersion optional version of package. If none, latest will be pulled in - * @returns string constructed with the arguments for dotnet add package - */ - public constructAddPackageArguments(projectUri: vscode.Uri, packageName: string, packageVersion?: string): string { - const projectPath = utils.getQuotedPath(projectUri.fsPath); - if (packageVersion) { - return ` add ${projectPath} package ${packageName} -v ${packageVersion}`; - } else { - // pull in the latest version of the package and allow prerelease versions - return ` add ${projectPath} package ${packageName} --prerelease`; - } - } - - /** - * Runs dotnet add package to add a package reference to the specified project. If the project already has a package reference - * for this package version, the project file won't get updated - * @param projectUri uri of project to add package to - * @param packageName name of package - * @param packageVersion optional version of package. If none, latest will be pulled in - */ - public async addPackage(projectUri: vscode.Uri, packageName: string, packageVersion?: string): Promise { - const addOptions: ShellCommandOptions = { - commandTitle: constants.addPackage, - argument: this.constructAddPackageArguments(projectUri, packageName, packageVersion) - }; - // Add package can be done with any version of .NET so skip the supported version check - await this.netCoreTool.runDotnetCommand(addOptions, true); - } - - /** - * Adds specified package to Azure Functions project the specified file is a part of - * @param fileUri uri of file to find the containing AF project of to add package reference to - * @param packageName package to add reference to - * @param packageVersion optional version of package. If none, latest will be pulled in - */ - public async addPackageToAFProjectContainingFile(fileUri: vscode.Uri, packageName: string, packageVersion?: string): Promise { - try { - const project = await azureFunctionsUtils.getAFProjectContainingFile(fileUri); - - // if no AF projects were found, an error gets thrown from getAFProjectContainingFile(). This check is temporary until - // multiple AF projects in the workspace is handled. That scenario returns undefined and shows an info message telling the - // user to make sure their project has the package reference - if (project) { - await this.addPackage(project, packageName, packageVersion); - } else { - void vscode.window.showInformationMessage(constants.addPackageReferenceMessage, constants.moreInformation).then((result) => { - if (result === constants.moreInformation) { - void vscode.env.openExternal(vscode.Uri.parse(constants.sqlBindingsHelpLink)); - } - }); - } - } catch (e) { - const result = await vscode.window.showErrorMessage(constants.addSqlBindingPackageError, constants.checkoutOutputMessage); - if (result === constants.checkoutOutputMessage) { - this._outputChannel.show(); - } - } - } -} - diff --git a/extensions/sql-database-projects/yarn.lock b/extensions/sql-database-projects/yarn.lock index c69988cffc..315de6d4fb 100644 --- a/extensions/sql-database-projects/yarn.lock +++ b/extensions/sql-database-projects/yarn.lock @@ -818,11 +818,6 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" -jsonc-parser@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" - integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== - jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"