mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Add sql bindings package reference to project (#16912)
* list packages and add packages working * cleanup and remove list packages * cleanup * change to pull in latest package version
This commit is contained in:
@@ -444,7 +444,9 @@ export function getTargetPlatformFromVersion(version: string): string {
|
|||||||
|
|
||||||
// Insert SQL binding
|
// Insert SQL binding
|
||||||
export const hostFileName = 'host.json';
|
export const hostFileName = 'host.json';
|
||||||
|
export const sqlExtensionPackageName = 'Microsoft.Azure.WebJobs.Extensions.Sql';
|
||||||
export const placeHolderObject = '[dbo].[table1]';
|
export const placeHolderObject = '[dbo].[table1]';
|
||||||
|
|
||||||
export const input = localize('input', "Input");
|
export const input = localize('input', "Input");
|
||||||
export const output = localize('output', "Output");
|
export const output = localize('output', "Output");
|
||||||
export const selectBindingType = localize('selectBindingType', "Select type of binding");
|
export const selectBindingType = localize('selectBindingType', "Select type of binding");
|
||||||
@@ -454,3 +456,5 @@ export const sqlTableToUpsert = localize('sqlTableToUpsert', "SQL table to upser
|
|||||||
export const connectionStringSetting = localize('connectionStringSetting', "Connection string setting name");
|
export const connectionStringSetting = localize('connectionStringSetting', "Connection string setting name");
|
||||||
export const connectionStringSettingPlaceholder = localize('connectionStringSettingPlaceholder', "Connection string setting specified in \"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 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");
|
||||||
|
|||||||
@@ -488,3 +488,20 @@ export async function retry<T>(
|
|||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)));
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ 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 { launchAddSqlBindingQuickpick } from '../dialogs/addSqlBindingQuickpick';
|
||||||
|
import { PackageHelper } from '../tools/packageHelper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main controller class that initializes the extension
|
* The main controller class that initializes the extension
|
||||||
@@ -23,11 +24,13 @@ import { launchAddSqlBindingQuickpick } from '../dialogs/addSqlBindingQuickpick'
|
|||||||
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 {
|
||||||
@@ -74,7 +77,7 @@ export default class MainController implements vscode.Disposable {
|
|||||||
vscode.commands.registerCommand('sqlDatabaseProjects.changeTargetPlatform', async (node: WorkspaceTreeItem) => { await this.projectsController.changeTargetPlatform(node); });
|
vscode.commands.registerCommand('sqlDatabaseProjects.changeTargetPlatform', async (node: WorkspaceTreeItem) => { await this.projectsController.changeTargetPlatform(node); });
|
||||||
vscode.commands.registerCommand('sqlDatabaseProjects.validateExternalStreamingJob', async (node: WorkspaceTreeItem) => { await this.projectsController.validateExternalStreamingJob(node); });
|
vscode.commands.registerCommand('sqlDatabaseProjects.validateExternalStreamingJob', async (node: WorkspaceTreeItem) => { await this.projectsController.validateExternalStreamingJob(node); });
|
||||||
|
|
||||||
vscode.commands.registerCommand('sqlDatabaseProjects.addSqlBinding', async (uri: vscode.Uri | undefined) => { await launchAddSqlBindingQuickpick(uri); });
|
vscode.commands.registerCommand('sqlDatabaseProjects.addSqlBinding', async (uri: vscode.Uri | undefined) => { await launchAddSqlBindingQuickpick(uri, this.packageHelper); });
|
||||||
|
|
||||||
IconPathHelper.setExtensionContext(this.extensionContext);
|
IconPathHelper.setExtensionContext(this.extensionContext);
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ import * as vscode from 'vscode';
|
|||||||
import { BindingType } from 'vscode-mssql';
|
import { BindingType } from 'vscode-mssql';
|
||||||
import * as constants from '../common/constants';
|
import * as constants from '../common/constants';
|
||||||
import * as utils from '../common/utils';
|
import * as utils from '../common/utils';
|
||||||
|
import { PackageHelper } from '../tools/packageHelper';
|
||||||
|
|
||||||
export async function launchAddSqlBindingQuickpick(uri: vscode.Uri | undefined): Promise<void> {
|
export async function launchAddSqlBindingQuickpick(uri: vscode.Uri | undefined, packageHelper: PackageHelper): Promise<void> {
|
||||||
if (!uri) {
|
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
|
// 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
|
// when this is called without a uri
|
||||||
@@ -95,5 +96,8 @@ export async function launchAddSqlBindingQuickpick(uri: vscode.Uri | undefined):
|
|||||||
vscode.window.showErrorMessage(e);
|
vscode.window.showErrorMessage(e);
|
||||||
return;
|
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.fsPath, constants.sqlExtensionPackageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
104
extensions/sql-database-projects/src/tools/packageHelper.ts
Normal file
104
extensions/sql-database-projects/src/tools/packageHelper.ts
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 '../common/utils';
|
||||||
|
import * as constants from '../common/constants';
|
||||||
|
import { DotNetCommandOptions, NetCoreTool } from './netcoreTool';
|
||||||
|
|
||||||
|
export class PackageHelper {
|
||||||
|
private netCoreTool: NetCoreTool;
|
||||||
|
|
||||||
|
constructor(outputChannel: vscode.OutputChannel) {
|
||||||
|
this.netCoreTool = new NetCoreTool(outputChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the parameters for a dotnet add package
|
||||||
|
* @param projectPath full path to 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(projectPath: string, packageName: string, packageVersion?: string): string {
|
||||||
|
projectPath = utils.getQuotedPath(projectPath);
|
||||||
|
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 projectPath full path to 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(project: string, packageName: string, packageVersion?: string): Promise<void> {
|
||||||
|
const addOptions: DotNetCommandOptions = {
|
||||||
|
commandTitle: constants.addPackage,
|
||||||
|
argument: this.constructAddPackageArguments(project, packageName, packageVersion)
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.netCoreTool.runDotnetCommand(addOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds specified package to Azure Functions project the specified file is a part of
|
||||||
|
* @param filePath full path to 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(filePath: string, packageName: string, packageVersion?: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const project = await this.getAFProjectContainingFile(filePath);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
vscode.window.showErrorMessage(e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Azure Functions project that contains the given file
|
||||||
|
* @param filePath file that the containing project needs to be found for
|
||||||
|
* @returns filepath of project or undefined if project couldn't be found
|
||||||
|
*/
|
||||||
|
public async getAFProjectContainingFile(filePath: string): Promise<string | 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 => this.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
|
||||||
|
vscode.window.showInformationMessage(`To use SQL bindings, ensure your Azure Functions project has a reference to ${constants.sqlExtensionPackageName}`);
|
||||||
|
console.error('need to find which project contains the file ' + filePath);
|
||||||
|
return undefined;
|
||||||
|
} else if (functionsProjects.length === 0) {
|
||||||
|
throw new Error(constants.noAzureFunctionsProjectsInWorkspace);
|
||||||
|
} else {
|
||||||
|
return functionsProjects[0].fsPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use 'host.json' as an indicator that this is a functions project
|
||||||
|
// copied from verifyIsproject.ts in vscode-azurefunctions extension
|
||||||
|
async isFunctionProject(folderPath: string): Promise<boolean> {
|
||||||
|
return await fse.pathExists(path.join(folderPath, constants.hostFileName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user