mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Remove sql-bindings logic from sql-database-projects (#18754)
* remove sql-bindings logic from sql-database-projects
This commit is contained in:
@@ -17,9 +17,7 @@
|
|||||||
"onCommand:sqlDatabaseProjects.open",
|
"onCommand:sqlDatabaseProjects.open",
|
||||||
"onCommand:sqlDatabaseProjects.createProjectFromDatabase",
|
"onCommand:sqlDatabaseProjects.createProjectFromDatabase",
|
||||||
"onCommand:sqlDatabaseProjects.updateProjectFromDatabase",
|
"onCommand:sqlDatabaseProjects.updateProjectFromDatabase",
|
||||||
"onCommand:sqlDatabaseProjects.addSqlBinding",
|
|
||||||
"onCommand:sqlDatabaseProjects.generateProjectFromOpenApiSpec",
|
"onCommand:sqlDatabaseProjects.generateProjectFromOpenApiSpec",
|
||||||
"onCommand:sqlDatabaseProjects.addSqlBinding",
|
|
||||||
"workspaceContains:**/*.sqlproj",
|
"workspaceContains:**/*.sqlproj",
|
||||||
"onView:dataworkspace.views.main"
|
"onView:dataworkspace.views.main"
|
||||||
],
|
],
|
||||||
@@ -178,11 +176,6 @@
|
|||||||
"title": "%sqlDatabaseProjects.editProjectFile%",
|
"title": "%sqlDatabaseProjects.editProjectFile%",
|
||||||
"category": "%sqlDatabaseProjects.displayName%"
|
"category": "%sqlDatabaseProjects.displayName%"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"command": "sqlDatabaseProjects.addSqlBinding",
|
|
||||||
"title": "%sqlDatabaseProjects.addSqlBinding%",
|
|
||||||
"category": "MS SQL"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"command": "sqlDatabaseProjects.changeTargetPlatform",
|
"command": "sqlDatabaseProjects.changeTargetPlatform",
|
||||||
"title": "%sqlDatabaseProjects.changeTargetPlatform%",
|
"title": "%sqlDatabaseProjects.changeTargetPlatform%",
|
||||||
@@ -296,10 +289,6 @@
|
|||||||
"command": "sqlDatabaseProjects.editProjectFile",
|
"command": "sqlDatabaseProjects.editProjectFile",
|
||||||
"when": "false"
|
"when": "false"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"command": "sqlDatabaseProjects.addSqlBinding",
|
|
||||||
"when": "editorLangId == csharp && !azdataAvailable && resourceScheme != untitled"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"command": "sqlDatabaseProjects.exclude",
|
"command": "sqlDatabaseProjects.exclude",
|
||||||
"when": "false"
|
"when": "false"
|
||||||
@@ -461,7 +450,6 @@
|
|||||||
"@microsoft/ads-extension-telemetry": "^1.1.5",
|
"@microsoft/ads-extension-telemetry": "^1.1.5",
|
||||||
"fast-glob": "^3.2.7",
|
"fast-glob": "^3.2.7",
|
||||||
"fs-extra": "^5.0.0",
|
"fs-extra": "^5.0.0",
|
||||||
"jsonc-parser": "^2.3.1",
|
|
||||||
"promisify-child-process": "^3.1.1",
|
"promisify-child-process": "^3.1.1",
|
||||||
"semver": "^7.3.5",
|
"semver": "^7.3.5",
|
||||||
"vscode-languageclient": "^5.3.0-next.1",
|
"vscode-languageclient": "^5.3.0-next.1",
|
||||||
|
|||||||
@@ -38,7 +38,5 @@
|
|||||||
"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.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)"
|
||||||
|
|
||||||
"sqlDatabaseProjects.addSqlBinding":"Add SQL Binding (preview)"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<ILocalSettingsJson> {
|
|
||||||
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<boolean> {
|
|
||||||
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<vscode.Uri | undefined> {
|
|
||||||
// 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<boolean> {
|
|
||||||
return fse.pathExists(path.join(folderPath, constants.hostFileName));
|
|
||||||
}
|
|
||||||
@@ -533,48 +533,3 @@ export const defaultDSP = targetPlatformToVersion.get(defaultTargetPlatform)!;
|
|||||||
export function getTargetPlatformFromVersion(version: string): string {
|
export function getTargetPlatformFromVersion(version: string): string {
|
||||||
return Array.from(targetPlatformToVersion.keys()).filter(k => targetPlatformToVersion.get(k) === version)[0];
|
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");
|
|
||||||
|
|||||||
@@ -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<T extends object>(data: string): T {
|
|
||||||
if (data.charCodeAt(0) === 0xFEFF) {
|
|
||||||
data = data.slice(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const errors: jsonc.ParseError[] = [];
|
|
||||||
const result: T = <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];
|
|
||||||
}
|
|
||||||
@@ -16,7 +16,6 @@ export enum TelemetryViews {
|
|||||||
ProjectController = 'ProjectController',
|
ProjectController = 'ProjectController',
|
||||||
SqlProjectPublishDialog = 'SqlProjectPublishDialog',
|
SqlProjectPublishDialog = 'SqlProjectPublishDialog',
|
||||||
ProjectTree = 'ProjectTree',
|
ProjectTree = 'ProjectTree',
|
||||||
SqlBindingsQuickPick = 'SqlBindingsQuickPick'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TelemetryActions {
|
export enum TelemetryActions {
|
||||||
@@ -33,7 +32,5 @@ export enum TelemetryActions {
|
|||||||
build = 'build',
|
build = 'build',
|
||||||
updateProjectForRoundtrip = 'updateProjectForRoundtrip',
|
updateProjectForRoundtrip = 'updateProjectForRoundtrip',
|
||||||
changePlatformType = 'changePlatformType',
|
changePlatformType = 'changePlatformType',
|
||||||
updateSystemDatabaseReferencesInProjFile = 'updateSystemDatabaseReferencesInProjFile',
|
updateSystemDatabaseReferencesInProjFile = 'updateSystemDatabaseReferencesInProjFile'
|
||||||
startAddSqlBinding = 'startAddSqlBinding',
|
|
||||||
finishAddSqlBinding = 'finishAddSqlBinding'
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -302,16 +302,6 @@ export async function getSchemaCompareService(): Promise<ISchemaCompareService>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAzureFunctionService(): Promise<vscodeMssql.IAzureFunctionsService> {
|
|
||||||
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<vscodeMssql.IExtension> {
|
export async function getVscodeMssqlApi(): Promise<vscodeMssql.IExtension> {
|
||||||
const ext = vscode.extensions.getExtension(vscodeMssql.extension.name) as vscode.Extension<vscodeMssql.IExtension>;
|
const ext = vscode.extensions.getExtension(vscodeMssql.extension.name) as vscode.Extension<vscodeMssql.IExtension>;
|
||||||
return ext.activate();
|
return ext.activate();
|
||||||
@@ -475,23 +465,6 @@ export async function detectCommandInstallation(command: string): Promise<boolea
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all the projects of the specified extension in the folder
|
|
||||||
* @param folder
|
|
||||||
* @param projectExtension project extension to filter on
|
|
||||||
* @returns array of project uris
|
|
||||||
*/
|
|
||||||
export async function getAllProjectsInFolder(folder: vscode.Uri, projectExtension: string): Promise<vscode.Uri[]> {
|
|
||||||
// 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 {
|
export function validateSqlServerPortNumber(port: string | undefined): boolean {
|
||||||
if (!port) {
|
if (!port) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ import { IconPathHelper } from '../common/iconHelper';
|
|||||||
import { WorkspaceTreeItem } from 'dataworkspace';
|
import { WorkspaceTreeItem } from 'dataworkspace';
|
||||||
import * as constants from '../common/constants';
|
import * as constants from '../common/constants';
|
||||||
import { SqlDatabaseProjectProvider } from '../projectProvider/projectProvider';
|
import { SqlDatabaseProjectProvider } from '../projectProvider/projectProvider';
|
||||||
import { launchAddSqlBindingQuickpick } from '../dialogs/addSqlBindingQuickpick';
|
|
||||||
import { PackageHelper } from '../tools/packageHelper';
|
|
||||||
import { GenerateProjectFromOpenApiSpecOptions } from 'sqldbproj';
|
import { GenerateProjectFromOpenApiSpecOptions } from 'sqldbproj';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,13 +24,11 @@ import { GenerateProjectFromOpenApiSpecOptions } from 'sqldbproj';
|
|||||||
export default class MainController implements vscode.Disposable {
|
export default class MainController implements vscode.Disposable {
|
||||||
protected projectsController: ProjectsController;
|
protected projectsController: ProjectsController;
|
||||||
protected netcoreTool: NetCoreTool;
|
protected netcoreTool: NetCoreTool;
|
||||||
protected packageHelper: PackageHelper;
|
|
||||||
private _outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel(constants.projectsOutputChannel);
|
private _outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel(constants.projectsOutputChannel);
|
||||||
|
|
||||||
public constructor(private context: vscode.ExtensionContext) {
|
public constructor(private context: vscode.ExtensionContext) {
|
||||||
this.projectsController = new ProjectsController(this._outputChannel);
|
this.projectsController = new ProjectsController(this._outputChannel);
|
||||||
this.netcoreTool = new NetCoreTool(this._outputChannel);
|
this.netcoreTool = new NetCoreTool(this._outputChannel);
|
||||||
this.packageHelper = new PackageHelper(this._outputChannel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public get extensionContext(): vscode.ExtensionContext {
|
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.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.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);
|
IconPathHelper.setExtensionContext(this.extensionContext);
|
||||||
|
|
||||||
await templates.loadTemplates(path.join(this.context.extensionPath, 'resources', 'templates'));
|
await templates.loadTemplates(path.join(this.context.extensionPath, 'resources', 'templates'));
|
||||||
|
|||||||
@@ -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<void> {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
@@ -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<void> {
|
|
||||||
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<void> {
|
|
||||||
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<void> {
|
|
||||||
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
|
|
||||||
}]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -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<void> {
|
|
||||||
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');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -8,16 +8,11 @@ import * as azdata from 'azdata';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as TypeMoq from 'typemoq';
|
import * as TypeMoq from 'typemoq';
|
||||||
import * as mssql from '../../../mssql/src/mssql';
|
import * as mssql from '../../../mssql/src/mssql';
|
||||||
import * as vscodeMssql from 'vscode-mssql';
|
|
||||||
|
|
||||||
export interface TestContext {
|
export interface TestContext {
|
||||||
context: vscode.ExtensionContext;
|
context: vscode.ExtensionContext;
|
||||||
dacFxService: TypeMoq.IMock<mssql.IDacFxService>;
|
dacFxService: TypeMoq.IMock<mssql.IDacFxService>;
|
||||||
azureFunctionService: TypeMoq.IMock<vscodeMssql.IAzureFunctionsService>;
|
|
||||||
outputChannel: vscode.OutputChannel;
|
outputChannel: vscode.OutputChannel;
|
||||||
vscodeMssqlIExtension: TypeMoq.IMock<vscodeMssql.IExtension>
|
|
||||||
dacFxMssqlService: TypeMoq.IMock<vscodeMssql.IDacFxService>;
|
|
||||||
schemaCompareService: TypeMoq.IMock<vscodeMssql.ISchemaCompareService>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mockDacFxResult = {
|
export const mockDacFxResult = {
|
||||||
@@ -124,153 +119,6 @@ export class MockDacFxService implements mssql.IDacFxService {
|
|||||||
public validateStreamingJob(_: string, __: string): Thenable<mssql.ValidateStreamingJobResult> { return Promise.resolve(mockDacFxResult); }
|
public validateStreamingJob(_: string, __: string): Thenable<mssql.ValidateStreamingJobResult> { 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<vscodeMssql.ResultStatus> { return Promise.resolve(mockResultStatus); }
|
|
||||||
getAzureFunctions(_: string): Thenable<vscodeMssql.GetAzureFunctionsResult> { 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<vscodeMssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
|
||||||
public importBacpac(_: string, __: string, ___: string, ____: vscodeMssql.TaskExecutionMode): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
|
||||||
public extractDacpac(_: string, __: string, ___: string, ____: string, _____: string, ______: vscodeMssql.TaskExecutionMode): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
|
||||||
public createProjectFromDatabase(_: string, __: string, ___: string, ____: string, _____: string, ______: vscodeMssql.ExtractTarget, _______: vscodeMssql.TaskExecutionMode): Thenable<vscodeMssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
|
||||||
public deployDacpac(_: string, __: string, ___: boolean, ____: string, _____: vscodeMssql.TaskExecutionMode, ______?: Record<string, string>): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
|
||||||
public generateDeployScript(_: string, __: string, ___: string, ____: vscodeMssql.TaskExecutionMode, ______?: Record<string, string>): Thenable<mssql.DacFxResult> { return Promise.resolve(mockDacFxResult); }
|
|
||||||
public generateDeployPlan(_: string, __: string, ___: string, ____: vscodeMssql.TaskExecutionMode): Thenable<vscodeMssql.GenerateDeployPlanResult> { return Promise.resolve(mockDacFxResult); }
|
|
||||||
public getOptionsFromProfile(_: string): Thenable<vscodeMssql.DacFxOptionsResult> { return Promise.resolve(mockDacFxMssqlOptionResult); }
|
|
||||||
public validateStreamingJob(_: string, __: string): Thenable<mssql.ValidateStreamingJobResult> { return Promise.resolve(mockDacFxResult); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MockSchemaCompareService implements vscodeMssql.ISchemaCompareService {
|
|
||||||
schemaCompareGetDefaultOptions(): Thenable<vscodeMssql.SchemaCompareOptionsResult> {
|
|
||||||
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<vscodeMssql.IConnectionInfo | undefined> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
connect(_: vscodeMssql.IConnectionInfo, __?: boolean): Promise<string> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
listDatabases(_: string): Promise<string[]> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
getDatabaseNameFromTreeNode(_: vscodeMssql.ITreeNodeInfo): string {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
getConnectionString(_: string | vscodeMssql.ConnectionDetails, ___?: boolean, _____?: boolean): Promise<string> {
|
|
||||||
throw new Error('Method not implemented.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createContext(): TestContext {
|
export function createContext(): TestContext {
|
||||||
let extensionPath = path.join(__dirname, '..', '..');
|
let extensionPath = path.join(__dirname, '..', '..');
|
||||||
|
|
||||||
@@ -303,10 +151,6 @@ export function createContext(): TestContext {
|
|||||||
extension: undefined as any
|
extension: undefined as any
|
||||||
},
|
},
|
||||||
dacFxService: TypeMoq.Mock.ofType(MockDacFxService),
|
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: {
|
outputChannel: {
|
||||||
name: '',
|
name: '',
|
||||||
append: () => { },
|
append: () => { },
|
||||||
@@ -342,41 +186,3 @@ export const mockConnectionProfile: azdata.IConnectionProfile = {
|
|||||||
connectionName: 'My Connection Name'
|
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;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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<void> {
|
|
||||||
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<void> {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -818,11 +818,6 @@ json5@^2.1.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.5"
|
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:
|
jsonfile@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
|
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
|
||||||
|
|||||||
Reference in New Issue
Block a user