mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
SQL Bindings - Adds Views support for Create Azure Function with SQL Binding (#19894)
* add views logic * add views tests * address PR and Figma comments * add ObjectType Enum to be used as a parameter * add comment
This commit is contained in:
@@ -56,7 +56,7 @@
|
|||||||
"view/item/context": [
|
"view/item/context": [
|
||||||
{
|
{
|
||||||
"command": "sqlBindings.createAzureFunction",
|
"command": "sqlBindings.createAzureFunction",
|
||||||
"when": "view == objectExplorer && viewItem == Table",
|
"when": "view == objectExplorer && (viewItem == Table || viewItem == View)",
|
||||||
"group": "zAzure_Function@1"
|
"group": "zAzure_Function@1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import * as path from 'path';
|
|||||||
import * as utils from './utils';
|
import * as utils from './utils';
|
||||||
import * as constants from './constants';
|
import * as constants from './constants';
|
||||||
import * as azureFunctionsContracts from '../contracts/azureFunctions/azureFunctionsContracts';
|
import * as azureFunctionsContracts from '../contracts/azureFunctions/azureFunctionsContracts';
|
||||||
import { BindingType, IConnectionStringInfo } from 'sql-bindings';
|
import { BindingType, IConnectionStringInfo, ObjectType } from 'sql-bindings';
|
||||||
import { ConnectionDetails, IConnectionInfo } from 'vscode-mssql';
|
import { ConnectionDetails, IConnectionInfo } from 'vscode-mssql';
|
||||||
// https://github.com/microsoft/vscode-azurefunctions/blob/main/src/vscode-azurefunctions.api.d.ts
|
// https://github.com/microsoft/vscode-azurefunctions/blob/main/src/vscode-azurefunctions.api.d.ts
|
||||||
import { AzureFunctionsExtensionApi } from '../../../types/vscode-azurefunctions.api';
|
import { AzureFunctionsExtensionApi } from '../../../types/vscode-azurefunctions.api';
|
||||||
@@ -286,9 +286,13 @@ export async function isFunctionProject(folderPath: string): Promise<boolean> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompts the user to select type of binding and returns result
|
* Prompts the user to select type of binding and returns result
|
||||||
|
* @param objectType (Optional) The type of object user choose to insert/upsert into
|
||||||
* @param funcName (Optional) Name of the function to which we are adding the SQL Binding
|
* @param funcName (Optional) Name of the function to which we are adding the SQL Binding
|
||||||
|
* @returns binding type or undefined if the user cancelled out of the prompt
|
||||||
*/
|
*/
|
||||||
export async function promptForBindingType(funcName?: string): Promise<BindingType | undefined> {
|
export async function promptForBindingType(objectType?: ObjectType, funcName?: string): Promise<BindingType | undefined> {
|
||||||
|
// check to see if objectType is view
|
||||||
|
let isView = (objectType === ObjectType.View);
|
||||||
const inputOutputItems: (vscode.QuickPickItem & { type: BindingType })[] = [
|
const inputOutputItems: (vscode.QuickPickItem & { type: BindingType })[] = [
|
||||||
{
|
{
|
||||||
label: constants.input,
|
label: constants.input,
|
||||||
@@ -302,7 +306,8 @@ export async function promptForBindingType(funcName?: string): Promise<BindingTy
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const selectedBinding = (await vscode.window.showQuickPick(inputOutputItems, {
|
// view can only be used with input binding
|
||||||
|
const selectedBinding = isView ? { type: BindingType.input } : (await vscode.window.showQuickPick(inputOutputItems, {
|
||||||
canPickMany: false,
|
canPickMany: false,
|
||||||
title: constants.selectBindingType(funcName),
|
title: constants.selectBindingType(funcName),
|
||||||
ignoreFocusOut: true
|
ignoreFocusOut: true
|
||||||
@@ -311,14 +316,30 @@ export async function promptForBindingType(funcName?: string): Promise<BindingTy
|
|||||||
return selectedBinding?.type;
|
return selectedBinding?.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompts the user to select to use a table or view as the object to query/upsert into
|
||||||
|
*/
|
||||||
|
export async function promptForObjectType(): Promise<ObjectType | undefined> {
|
||||||
|
const objectTypes: (vscode.QuickPickItem & { type: ObjectType })[] =
|
||||||
|
[{ label: constants.table, type: ObjectType.Table }, { label: constants.view, type: ObjectType.View }];
|
||||||
|
const selectedObjectType = (await vscode.window.showQuickPick(objectTypes, {
|
||||||
|
canPickMany: false,
|
||||||
|
title: constants.selectSqlTableOrViewPrompt,
|
||||||
|
ignoreFocusOut: true
|
||||||
|
}));
|
||||||
|
|
||||||
|
return selectedObjectType?.type;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompts the user to enter object name for the SQL query
|
* Prompts the user to enter object name for the SQL query
|
||||||
* @param bindingType Type of SQL Binding
|
* @param bindingType Type of SQL Binding
|
||||||
* @param connectionInfo (optional) connection info from the selected connection profile
|
* @param connectionInfo (optional) connection info from the selected connection profile
|
||||||
* if left undefined we prompt to manually enter the object name
|
* if left undefined we prompt to manually enter the object name
|
||||||
|
* @param objectType (optional) type of object to query/upsert into
|
||||||
* @returns the object name from user's input or menu choice
|
* @returns the object name from user's input or menu choice
|
||||||
*/
|
*/
|
||||||
export async function promptForObjectName(bindingType: BindingType, connectionInfo?: IConnectionInfo): Promise<string | undefined> {
|
export async function promptForObjectName(bindingType: BindingType, connectionInfo?: IConnectionInfo, objectType?: ObjectType): Promise<string | undefined> {
|
||||||
// show the connection string methods (user input and connection profile options)
|
// show the connection string methods (user input and connection profile options)
|
||||||
let connectionURI: string | undefined;
|
let connectionURI: string | undefined;
|
||||||
let selectedDatabase: string | undefined;
|
let selectedDatabase: string | undefined;
|
||||||
@@ -329,9 +350,8 @@ export async function promptForObjectName(bindingType: BindingType, connectionIn
|
|||||||
return promptToManuallyEnterObjectName(bindingType);
|
return promptToManuallyEnterObjectName(bindingType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO create path to solve for selectView (first need to support views as well)
|
// Prompt user to select a table/view based on connection profile and selected database
|
||||||
// Prompt user to select a table based on connection profile and selected database}
|
// get connectionURI and selectedDatabase to be used for listing tables/view query request
|
||||||
// get connectionURI and selectedDatabase to be used for listing tables query request
|
|
||||||
connectionURI = await getConnectionURI(connectionInfo);
|
connectionURI = await getConnectionURI(connectionInfo);
|
||||||
if (!connectionURI) {
|
if (!connectionURI) {
|
||||||
// mssql connection error
|
// mssql connection error
|
||||||
@@ -345,7 +365,7 @@ export async function promptForObjectName(bindingType: BindingType, connectionIn
|
|||||||
|
|
||||||
connectionInfo.database = selectedDatabase;
|
connectionInfo.database = selectedDatabase;
|
||||||
|
|
||||||
let selectedObjectName = await promptSelectTable(connectionURI, bindingType, selectedDatabase);
|
let selectedObjectName = await promptSelectObject(connectionURI, bindingType, selectedDatabase, objectType);
|
||||||
|
|
||||||
return selectedObjectName;
|
return selectedObjectName;
|
||||||
}
|
}
|
||||||
@@ -618,35 +638,38 @@ export async function getConnectionURI(connectionInfo: IConnectionInfo): Promise
|
|||||||
return connectionURI;
|
return connectionURI;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function promptSelectTable(connectionURI: string, bindingType: BindingType, selectedDatabase: string): Promise<string | undefined> {
|
export async function promptSelectObject(connectionURI: string, bindingType: BindingType, selectedDatabase: string, objectType?: string): Promise<string | undefined> {
|
||||||
const vscodeMssqlApi = await utils.getVscodeMssqlApi();
|
const vscodeMssqlApi = await utils.getVscodeMssqlApi();
|
||||||
const userObjectName = bindingType === BindingType.input ? constants.enterObjectName : constants.enterObjectNameToUpsert;
|
let isView = (objectType === ObjectType.View);
|
||||||
|
|
||||||
// Create query to get list of tables from database selected
|
const userObjectName = isView ? constants.enterViewName : bindingType === BindingType.input ? constants.enterTableName : constants.enterTableNameToUpsert;
|
||||||
let tableQuery = tablesQuery(selectedDatabase);
|
|
||||||
const params = { ownerUri: connectionURI, queryString: tableQuery };
|
// Create query to get list of tables or views from selected database
|
||||||
|
let listQuery = isView ? viewsQuery(selectedDatabase) : tablesQuery(selectedDatabase);
|
||||||
|
const params = { ownerUri: connectionURI, queryString: listQuery };
|
||||||
let queryResult: azureFunctionsContracts.SimpleExecuteResult | undefined;
|
let queryResult: azureFunctionsContracts.SimpleExecuteResult | undefined;
|
||||||
|
|
||||||
// send SimpleExecuteRequest query to STS to get list of schema and tables based on the connection profile and database of the user
|
// send SimpleExecuteRequest query to STS to get list of schema and tables based on the connection profile and database of the user
|
||||||
await vscode.window.withProgress(
|
await vscode.window.withProgress(
|
||||||
{
|
{
|
||||||
location: vscode.ProgressLocation.Notification,
|
location: vscode.ProgressLocation.Notification,
|
||||||
title: constants.tableListProgressTitle,
|
title: isView ? constants.viewListProgressTitle : constants.tableListProgressTitle,
|
||||||
cancellable: false
|
cancellable: false
|
||||||
}, async (_progress, _token) => {
|
}, async (_progress, _token) => {
|
||||||
queryResult = await vscodeMssqlApi.sendRequest(azureFunctionsContracts.SimpleExecuteRequest.type, params);
|
queryResult = await vscodeMssqlApi.sendRequest(azureFunctionsContracts.SimpleExecuteRequest.type, params);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get schema and table names from query result rows
|
// Get schema and table/view names from query result rows
|
||||||
const tableNames = queryResult!.rows.map(r => r[0].displayValue);
|
const objectNames = queryResult!.rows.map(r => r[0].displayValue);
|
||||||
// add manual entry option to table names list for user to choose from as well (with pencil icon)
|
// add manual entry option to object names list for user to choose from as well (with pencil icon)
|
||||||
let manuallyEnterObjectName = constants.manuallyEnterObjectName(userObjectName);
|
let manuallyEnterObjectName = constants.manuallyEnterObjectName(userObjectName);
|
||||||
tableNames.unshift(manuallyEnterObjectName);
|
objectNames.unshift(manuallyEnterObjectName);
|
||||||
// prompt user to select table from list of tables options
|
// prompt user to select object from list of objects
|
||||||
while (true) {
|
while (true) {
|
||||||
let selectedObject = await vscode.window.showQuickPick(tableNames, {
|
let selectedObject = await vscode.window.showQuickPick(objectNames, {
|
||||||
canPickMany: false,
|
canPickMany: false,
|
||||||
title: constants.selectTable,
|
title: isView ? constants.selectView : constants.selectTable,
|
||||||
ignoreFocusOut: true
|
ignoreFocusOut: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -667,10 +690,15 @@ export function tablesQuery(selectedDatabase: string): string {
|
|||||||
return `SELECT CONCAT(QUOTENAME(table_schema),'.',QUOTENAME(table_name)) from ${quotedDatabase}.INFORMATION_SCHEMA.TABLES where TABLE_TYPE = 'BASE TABLE'`;
|
return `SELECT CONCAT(QUOTENAME(table_schema),'.',QUOTENAME(table_name)) from ${quotedDatabase}.INFORMATION_SCHEMA.TABLES where TABLE_TYPE = 'BASE TABLE'`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function viewsQuery(selectedDatabase: string): string {
|
||||||
|
let quotedDatabase = '[' + utils.escapeClosingBrackets(selectedDatabase) + ']';
|
||||||
|
return `SELECT CONCAT(QUOTENAME(table_schema),'.',QUOTENAME(table_name)) from ${quotedDatabase}.INFORMATION_SCHEMA.VIEWS;`;
|
||||||
|
}
|
||||||
|
|
||||||
export async function promptToManuallyEnterObjectName(bindingType: BindingType): Promise<string | undefined> {
|
export async function promptToManuallyEnterObjectName(bindingType: BindingType): Promise<string | undefined> {
|
||||||
// user manually enters table or view to query or upsert into
|
// user manually enters table or view to query or upsert into
|
||||||
let selectedObject = await vscode.window.showInputBox({
|
let selectedObject = await vscode.window.showInputBox({
|
||||||
prompt: bindingType === BindingType.input ? constants.sqlTableOrViewToQuery : constants.sqlTableToUpsert,
|
prompt: bindingType === BindingType.input ? constants.sqlObjectToQuery : constants.sqlTableToUpsert,
|
||||||
placeHolder: constants.placeHolderObject,
|
placeHolder: constants.placeHolderObject,
|
||||||
validateInput: input => input ? undefined : constants.nameMustNotBeEmpty,
|
validateInput: input => input ? undefined : constants.nameMustNotBeEmpty,
|
||||||
ignoreFocusOut: true
|
ignoreFocusOut: true
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export const output = localize('output', "Output");
|
|||||||
export const inputDescription = localize('inputDescription', "Retrieves data from a database");
|
export const inputDescription = localize('inputDescription', "Retrieves data from a database");
|
||||||
export const outputDescription = localize('outputDescription', "Save data to a database");
|
export const outputDescription = localize('outputDescription', "Save data to a database");
|
||||||
export const selectAzureFunction = localize('selectAzureFunction', "Select an Azure function in the current file to add SQL binding to");
|
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 sqlObjectToQuery = localize('sqlObjectToQuery', "SQL object to query");
|
||||||
export const sqlTableToUpsert = localize('sqlTableToUpsert', "SQL table to upsert into");
|
export const sqlTableToUpsert = localize('sqlTableToUpsert', "SQL table to upsert into");
|
||||||
export const connectionStringSetting = localize('connectionStringSetting', "Connection string setting name");
|
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 selectSetting = localize('selectSetting', "Select SQL connection string setting from local.settings.json");
|
||||||
@@ -74,10 +74,16 @@ export const openFile = localize('openFile', "Open File");
|
|||||||
export const closeButton = localize('closeButton', "Close");
|
export const closeButton = localize('closeButton', "Close");
|
||||||
export const enterPasswordPrompt = localize('enterPasswordPrompt', '(Optional) Enter connection password to save in local.settings.json');
|
export const enterPasswordPrompt = localize('enterPasswordPrompt', '(Optional) Enter connection password to save in local.settings.json');
|
||||||
export const connectionProgressTitle = localize('connectionProgressTitle', "Testing SQL Server connection...");
|
export const connectionProgressTitle = localize('connectionProgressTitle', "Testing SQL Server connection...");
|
||||||
export const enterObjectName = localize('enterObjectName', 'Enter SQL table or view to query');
|
export const enterTableName = localize('enterTableName', 'Enter SQL table to query');
|
||||||
export const enterObjectNameToUpsert = localize('enterObjectNameToUpsert', 'Enter SQL table to upsert into');
|
export const enterViewName = localize('enterViewName', 'Enter SQL view to query');
|
||||||
|
export const enterTableNameToUpsert = localize('enterTableNameToUpsert', 'Enter SQL table to upsert into');
|
||||||
export const selectTable = localize('selectTable', 'Select table to use');
|
export const selectTable = localize('selectTable', 'Select table to use');
|
||||||
|
export const selectView = localize('selectView', 'Select view to use');
|
||||||
export const tableListProgressTitle = localize('tableListProgressTitle', "Fetching tables for selected database...");
|
export const tableListProgressTitle = localize('tableListProgressTitle', "Fetching tables for selected database...");
|
||||||
|
export const viewListProgressTitle = localize('viewListProgressTitle', "Fetching views for selected database...");
|
||||||
|
export const table = localize('table', 'Table');
|
||||||
|
export const view = localize('view', 'View');
|
||||||
|
export const selectSqlTableOrViewPrompt = localize('selectSqlTableOrViewPrompt', "Select object type to insert or upsert into");
|
||||||
export const failedToSetSetting = (err?: any): string => err ? localize('failedToSetSetting', "Failed to set connection string app setting: {0}", utils.getErrorMessage(err)) : localize('unableToSetConnectionString', "Failed to set connection string app setting");
|
export const failedToSetSetting = (err?: any): string => err ? localize('failedToSetSetting', "Failed to set connection string app setting: {0}", utils.getErrorMessage(err)) : localize('unableToSetConnectionString', "Failed to set connection string app setting");
|
||||||
export function selectBindingType(funcName?: string): string { return funcName ? localize('selectBindingTypeToSpecifiedFunction', "Select type of binding for the function '{0}'", funcName) : localize('selectBindingType', "Select type of binding"); }
|
export function selectBindingType(funcName?: string): string { return funcName ? localize('selectBindingTypeToSpecifiedFunction', "Select type of binding for the function '{0}'", funcName) : localize('selectBindingType', "Select type of binding"); }
|
||||||
export function settingAlreadyExists(settingName: string): string { return localize('SettingAlreadyExists', 'Local app setting \'{0}\' already exists. Overwrite?', settingName); }
|
export function settingAlreadyExists(settingName: string): string { return localize('SettingAlreadyExists', 'Local app setting \'{0}\' already exists. Overwrite?', settingName); }
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export enum CreateAzureFunctionStep {
|
|||||||
getSelectedFolder = 'getSelectedFolder',
|
getSelectedFolder = 'getSelectedFolder',
|
||||||
getBindingType = 'getBindingType',
|
getBindingType = 'getBindingType',
|
||||||
launchFromCommandPalette = 'launchFromCommandPalette',
|
launchFromCommandPalette = 'launchFromCommandPalette',
|
||||||
launchFromTable = 'launchFromTable',
|
launchFromObjectExplorer = 'launchFromObjectExplorer',
|
||||||
getConnectionProfile = 'getConnectionProfile',
|
getConnectionProfile = 'getConnectionProfile',
|
||||||
getDatabase = 'getDatabase',
|
getDatabase = 'getDatabase',
|
||||||
getObjectName = 'getObjectName',
|
getObjectName = 'getObjectName',
|
||||||
@@ -54,4 +54,4 @@ export enum ExitReason {
|
|||||||
timeout = 'timeout',
|
timeout = 'timeout',
|
||||||
error = 'error',
|
error = 'error',
|
||||||
exit = 'exit'
|
exit = 'exit'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { IConnectionInfo, ITreeNodeInfo } from 'vscode-mssql';
|
import { IConnectionInfo, ITreeNodeInfo } from 'vscode-mssql';
|
||||||
import { IExtension, BindingType, GetAzureFunctionsResult, ResultStatus, IConnectionStringInfo } from 'sql-bindings';
|
import { IExtension, BindingType, GetAzureFunctionsResult, ResultStatus, IConnectionStringInfo, ObjectType } from 'sql-bindings';
|
||||||
import { addSqlBinding, createAzureFunction, getAzureFunctions } from './services/azureFunctionsService';
|
import { addSqlBinding, createAzureFunction, getAzureFunctions } from './services/azureFunctionsService';
|
||||||
import { launchAddSqlBindingQuickpick } from './dialogs/addSqlBindingQuickpick';
|
import { launchAddSqlBindingQuickpick } from './dialogs/addSqlBindingQuickpick';
|
||||||
import { promptForBindingType, promptAndUpdateConnectionStringSetting, promptForObjectName, addSqlNugetReferenceToProjectFile } from './common/azureFunctionsUtils';
|
import { promptForBindingType, promptAndUpdateConnectionStringSetting, promptForObjectName, addSqlNugetReferenceToProjectFile } from './common/azureFunctionsUtils';
|
||||||
@@ -23,11 +23,11 @@ export async function activate(context: vscode.ExtensionContext): Promise<IExten
|
|||||||
createAzureFunction: async (): Promise<void> => {
|
createAzureFunction: async (): Promise<void> => {
|
||||||
return createAzureFunction();
|
return createAzureFunction();
|
||||||
},
|
},
|
||||||
promptForBindingType: async (funcName?: string): Promise<BindingType | undefined> => {
|
promptForBindingType: async (objectType?: ObjectType, funcName?: string): Promise<BindingType | undefined> => {
|
||||||
return promptForBindingType(funcName);
|
return promptForBindingType(objectType, funcName);
|
||||||
},
|
},
|
||||||
promptForObjectName: async (bindingType: BindingType, connectionInfo?: IConnectionInfo): Promise<string | undefined> => {
|
promptForObjectName: async (bindingType: BindingType, connectionInfo?: IConnectionInfo, objectType?: ObjectType): Promise<string | undefined> => {
|
||||||
return promptForObjectName(bindingType, connectionInfo);
|
return promptForObjectName(bindingType, connectionInfo, objectType);
|
||||||
},
|
},
|
||||||
promptAndUpdateConnectionStringSetting: async (projectUri: vscode.Uri | undefined, connectionInfo?: IConnectionInfo): Promise<IConnectionStringInfo | undefined> => {
|
promptAndUpdateConnectionStringSetting: async (projectUri: vscode.Uri | undefined, connectionInfo?: IConnectionInfo): Promise<IConnectionStringInfo | undefined> => {
|
||||||
return promptAndUpdateConnectionStringSetting(projectUri, connectionInfo);
|
return promptAndUpdateConnectionStringSetting(projectUri, connectionInfo);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import * as azureFunctionsUtils from '../common/azureFunctionsUtils';
|
|||||||
import * as constants from '../common/constants';
|
import * as constants from '../common/constants';
|
||||||
import * as azureFunctionsContracts from '../contracts/azureFunctions/azureFunctionsContracts';
|
import * as azureFunctionsContracts from '../contracts/azureFunctions/azureFunctionsContracts';
|
||||||
import { CreateAzureFunctionStep, TelemetryActions, TelemetryReporter, TelemetryViews, ExitReason } from '../common/telemetry';
|
import { CreateAzureFunctionStep, TelemetryActions, TelemetryReporter, TelemetryViews, ExitReason } from '../common/telemetry';
|
||||||
import { AddSqlBindingParams, BindingType, GetAzureFunctionsParams, GetAzureFunctionsResult, IConnectionStringInfo, ResultStatus } from 'sql-bindings';
|
import { AddSqlBindingParams, BindingType, GetAzureFunctionsParams, GetAzureFunctionsResult, IConnectionStringInfo, ObjectType, ResultStatus } from 'sql-bindings';
|
||||||
import { IConnectionInfo, ITreeNodeInfo } from 'vscode-mssql';
|
import { IConnectionInfo, ITreeNodeInfo } from 'vscode-mssql';
|
||||||
import { createAddConnectionStringStep } from '../createNewProject/addConnectionStringStep';
|
import { createAddConnectionStringStep } from '../createNewProject/addConnectionStringStep';
|
||||||
|
|
||||||
@@ -99,23 +99,32 @@ export async function createAzureFunction(node?: ITreeNodeInfo): Promise<void> {
|
|||||||
// create a system file watcher for the project folder
|
// create a system file watcher for the project folder
|
||||||
newFunctionFileObject = azureFunctionsUtils.waitForNewFunctionFile(projectFolder);
|
newFunctionFileObject = azureFunctionsUtils.waitForNewFunctionFile(projectFolder);
|
||||||
|
|
||||||
// Prompt user for binding type
|
|
||||||
telemetryStep = CreateAzureFunctionStep.getBindingType;
|
|
||||||
let selectedBindingType: BindingType | undefined;
|
|
||||||
let selectedBinding = await azureFunctionsUtils.promptForBindingType();
|
|
||||||
if (!selectedBinding) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
selectedBindingType = selectedBinding;
|
|
||||||
propertyBag.bindingType = selectedBindingType;
|
|
||||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, telemetryStep)
|
|
||||||
.withAdditionalProperties(propertyBag).send();
|
|
||||||
|
|
||||||
// Get connection string parameters and construct object name from prompt or connectionInfo given
|
// Get connection string parameters and construct object name from prompt or connectionInfo given
|
||||||
let objectName: string | undefined;
|
let objectName: string | undefined;
|
||||||
|
let selectedBindingType: BindingType | undefined;
|
||||||
if (!node) {
|
if (!node) {
|
||||||
// if user selects command in command palette we prompt user for information
|
// user selects command in command palette we prompt user for information
|
||||||
telemetryStep = CreateAzureFunctionStep.launchFromCommandPalette;
|
telemetryStep = CreateAzureFunctionStep.launchFromCommandPalette;
|
||||||
|
|
||||||
|
let chosenObjectType = await azureFunctionsUtils.promptForObjectType();
|
||||||
|
if (!chosenObjectType) {
|
||||||
|
// User cancelled
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prompt user for binding type
|
||||||
|
telemetryStep = CreateAzureFunctionStep.getBindingType;
|
||||||
|
selectedBindingType = await azureFunctionsUtils.promptForBindingType(chosenObjectType);
|
||||||
|
if (!selectedBindingType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send telemetry for chosen object type and binding type
|
||||||
|
propertyBag.objectType = chosenObjectType;
|
||||||
|
propertyBag.bindingType = selectedBindingType;
|
||||||
|
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, telemetryStep)
|
||||||
|
.withAdditionalProperties(propertyBag).send();
|
||||||
|
|
||||||
// prompt user for connection profile to get connection info
|
// prompt user for connection profile to get connection info
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
@@ -135,7 +144,7 @@ export async function createAzureFunction(node?: ITreeNodeInfo): Promise<void> {
|
|||||||
telemetryStep = CreateAzureFunctionStep.getObjectName;
|
telemetryStep = CreateAzureFunctionStep.getObjectName;
|
||||||
|
|
||||||
// prompt user for object name to create function from
|
// prompt user for object name to create function from
|
||||||
objectName = await azureFunctionsUtils.promptForObjectName(selectedBinding, connectionInfo);
|
objectName = await azureFunctionsUtils.promptForObjectName(selectedBindingType, connectionInfo, chosenObjectType);
|
||||||
if (!objectName) {
|
if (!objectName) {
|
||||||
// user cancelled
|
// user cancelled
|
||||||
continue;
|
continue;
|
||||||
@@ -143,12 +152,12 @@ export async function createAzureFunction(node?: ITreeNodeInfo): Promise<void> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if user selects table in tree view we use connection info from Object Explorer node
|
// user selects table in tree view we use connection info from Object Explorer node
|
||||||
telemetryStep = CreateAzureFunctionStep.launchFromTable;
|
telemetryStep = CreateAzureFunctionStep.launchFromObjectExplorer;
|
||||||
connectionInfo = node.connectionInfo;
|
connectionInfo = node.connectionInfo;
|
||||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, telemetryStep)
|
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, telemetryStep)
|
||||||
.withAdditionalProperties(propertyBag).withConnectionInfo(connectionInfo).send();
|
.withAdditionalProperties(propertyBag).withConnectionInfo(connectionInfo).send();
|
||||||
// set the database containing the selected table so it can be used
|
// set the database containing the selected table or view so it can be used
|
||||||
// for the initial catalog property of the connection string
|
// for the initial catalog property of the connection string
|
||||||
let newNode: ITreeNodeInfo = node;
|
let newNode: ITreeNodeInfo = node;
|
||||||
while (newNode) {
|
while (newNode) {
|
||||||
@@ -159,6 +168,21 @@ export async function createAzureFunction(node?: ITreeNodeInfo): Promise<void> {
|
|||||||
newNode = newNode.parentNode;
|
newNode = newNode.parentNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prompt user for binding type
|
||||||
|
telemetryStep = CreateAzureFunctionStep.getBindingType;
|
||||||
|
let nodeType = ObjectType.Table === node.nodeType ? ObjectType.Table : ObjectType.View;
|
||||||
|
selectedBindingType = await azureFunctionsUtils.promptForBindingType(nodeType);
|
||||||
|
if (!selectedBindingType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// send telemetry for object type and binding type
|
||||||
|
propertyBag.objectType = node.nodeType;
|
||||||
|
propertyBag.bindingType = selectedBindingType;
|
||||||
|
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, telemetryStep)
|
||||||
|
.withAdditionalProperties(propertyBag).send();
|
||||||
|
|
||||||
objectName = utils.generateQuotedFullName(node.metadata.schema, node.metadata.name);
|
objectName = utils.generateQuotedFullName(node.metadata.schema, node.metadata.name);
|
||||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, telemetryStep)
|
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, telemetryStep)
|
||||||
.withAdditionalProperties(propertyBag).withConnectionInfo(connectionInfo).send();
|
.withAdditionalProperties(propertyBag).withConnectionInfo(connectionInfo).send();
|
||||||
|
|||||||
20
extensions/sql-bindings/src/sql-bindings.d.ts
vendored
20
extensions/sql-bindings/src/sql-bindings.d.ts
vendored
@@ -3,7 +3,6 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
declare module 'sql-bindings' {
|
declare module 'sql-bindings' {
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
@@ -14,6 +13,11 @@ declare module 'sql-bindings' {
|
|||||||
vsCodeName = 'ms-mssql.sql-bindings-vscode'
|
vsCodeName = 'ms-mssql.sql-bindings-vscode'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum ObjectType {
|
||||||
|
Table = 'Table',
|
||||||
|
View = 'View'
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sql bindings extension
|
* sql bindings extension
|
||||||
*/
|
*/
|
||||||
@@ -34,19 +38,24 @@ declare module 'sql-bindings' {
|
|||||||
createAzureFunction(): Promise<void>;
|
createAzureFunction(): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompts the user to select type of binding and returns result or undefined if the user cancelled out of the prompt
|
* Prompts the user to select type of binding and returns result
|
||||||
* @param funcName (Optional) Name of the function we are adding the SQL Binding to
|
* @param objectType (Optional) The type of object user choose to insert/upsert into
|
||||||
|
* if left undefined we prompt user to choose between input or output binding types
|
||||||
|
* @param funcName (Optional) Name of the function to which we are adding the SQL Binding
|
||||||
|
* @returns binding type or undefined if the user cancelled out of the prompt
|
||||||
*/
|
*/
|
||||||
promptForBindingType(funcName?: string): Promise<BindingType | undefined>;
|
promptForBindingType(objectType?: ObjectType, funcName?: string): Promise<BindingType | undefined>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompts the user to enter object name for the SQL query
|
* Prompts the user to enter object name for the SQL query
|
||||||
* @param bindingType Type of SQL Binding
|
* @param bindingType Type of SQL Binding
|
||||||
* @param connectionInfo (optional) connection info from the selected connection profile
|
* @param connectionInfo (optional) connection info from the selected connection profile
|
||||||
* if left undefined we prompt to manually enter the object name
|
* if left undefined we prompt to manually enter the object name
|
||||||
|
* @param objectType (optional) type of object to query/upsert into
|
||||||
|
* if left undefined we prompt user to select table to use or manually enter object name
|
||||||
* @returns the object name from user's input or menu choice
|
* @returns the object name from user's input or menu choice
|
||||||
*/
|
*/
|
||||||
promptForObjectName(bindingType: BindingType, connectionInfo?: IConnectionInfo): Promise<string | undefined>;
|
promptForObjectName(bindingType: BindingType, connectionInfo?: IConnectionInfo, objectType?: ObjectType): Promise<string | undefined>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompts the user to enter connection setting and updates it from AF project
|
* Prompts the user to enter connection setting and updates it from AF project
|
||||||
@@ -168,5 +177,4 @@ declare module 'sql-bindings' {
|
|||||||
connectionStringSettingName: string;
|
connectionStringSettingName: string;
|
||||||
connectionInfo: IConnectionInfo | undefined;
|
connectionInfo: IConnectionInfo | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import * as utils from '../../common/utils';
|
|||||||
import * as azureFunctionsContracts from '../../contracts/azureFunctions/azureFunctionsContracts';
|
import * as azureFunctionsContracts from '../../contracts/azureFunctions/azureFunctionsContracts';
|
||||||
import * as azureFunctionService from '../../services/azureFunctionsService';
|
import * as azureFunctionService from '../../services/azureFunctionsService';
|
||||||
|
|
||||||
import { BindingType } from 'sql-bindings';
|
import { BindingType, ObjectType } from 'sql-bindings';
|
||||||
import { IConnectionInfo } from 'vscode-mssql';
|
import { ConnectionDetails, IConnectionInfo } from 'vscode-mssql';
|
||||||
import { createTestCredentials, createTestTableNode, createTestUtils, TestUtils } from '../testUtils';
|
import { createTestCredentials, createTestTableNode, createTestUtils, TestUtils } from '../testUtils';
|
||||||
|
|
||||||
const rootFolderPath = 'test';
|
const rootFolderPath = 'test';
|
||||||
@@ -36,118 +36,6 @@ describe('AzureFunctionsService', () => {
|
|||||||
constants.install, constants.learnMore, constants.doNotInstall]);
|
constants.install, constants.learnMore, constants.doNotInstall]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should create azure function project using the command from command palette (no connection info)', async function (): Promise<void> {
|
|
||||||
// This test will have an azure function project already in the project and the azure functions extension installed (stubbed)
|
|
||||||
sinon.stub(azureFunctionUtils, 'getAzureFunctionsExtensionApi').resolves(testUtils.azureFunctionsExtensionApi.object); // set azure functions extension api
|
|
||||||
sinon.stub(azureFunctionUtils, 'getAzureFunctionProject').resolves(projectFilePath); //set azure function project to have one project
|
|
||||||
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
|
||||||
|
|
||||||
// create fake connection string settings for local.setting.json to be used
|
|
||||||
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
|
|
||||||
sinon.stub(fs, 'readFileSync').withArgs(sinon.match.any).returns(
|
|
||||||
`{"IsEncrypted": false,
|
|
||||||
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
|
|
||||||
);
|
|
||||||
|
|
||||||
let connectionInfo: IConnectionInfo = createTestCredentials(); // create test connectionInfo
|
|
||||||
|
|
||||||
let connectionDetails = { options: connectionInfo };
|
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
|
||||||
|
|
||||||
const showErrorMessageSpy = sinon.spy(vscode.window, 'showErrorMessage');
|
|
||||||
|
|
||||||
// select input or output binding
|
|
||||||
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves(<any>{ label: constants.input, type: BindingType.input });
|
|
||||||
|
|
||||||
// no table used for connection info so prompt user to get connection info
|
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.promptForConnection(true)).returns(() => Promise.resolve(connectionInfo));
|
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.connect(connectionInfo)).returns(() => Promise.resolve('testConnectionURI'));
|
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.listDatabases('testConnectionURI')).returns(() => Promise.resolve(['testDb']));
|
|
||||||
// select the testDB from list of databases based on connection info
|
|
||||||
quickPickStub.onSecondCall().resolves(('testDb') as any);
|
|
||||||
// get tables from selected database
|
|
||||||
const params = { ownerUri: 'testConnectionURI', queryString: azureFunctionUtils.tablesQuery('testDb') };
|
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.sendRequest(azureFunctionsContracts.SimpleExecuteRequest.type, params))
|
|
||||||
.returns(() => Promise.resolve({ rowCount: 1, columnInfo: [], rows: [[{ displayValue: '[schema].[testTable]' }]] }));
|
|
||||||
// select the schema.testTable from list of tables based on connection info and database
|
|
||||||
quickPickStub.onThirdCall().resolves(('[schema].[testTable]') as any);
|
|
||||||
|
|
||||||
// set azure function name
|
|
||||||
let inputStub = sinon.stub(vscode.window, 'showInputBox').resolves('testFunctionName');
|
|
||||||
|
|
||||||
// promptAndUpdateConnectionStringSetting
|
|
||||||
quickPickStub.onCall(3).resolves(<any>{ label: constants.createNewLocalAppSettingWithIcon });
|
|
||||||
inputStub.onSecondCall().resolves('SqlConnectionString');
|
|
||||||
// promptConnectionStringPasswordAndUpdateConnectionString - tested in AzureFunctionUtils.test.ts
|
|
||||||
quickPickStub.onCall(4).resolves((constants.yesString) as any);
|
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
|
||||||
// setLocalAppSetting with connection string setting name and connection string
|
|
||||||
// fails if we dont set writeFile stub
|
|
||||||
sinon.stub(fs.promises, 'writeFile').resolves();
|
|
||||||
sinon.stub(azureFunctionUtils, 'setLocalAppSetting').withArgs(sinon.match.any, 'SqlConnectionString', 'testConnectionString').resolves((true));
|
|
||||||
sinon.stub(utils, 'executeCommand').resolves('downloaded nuget package');
|
|
||||||
|
|
||||||
const testWatcher = TypeMoq.Mock.ofType<vscode.FileSystemWatcher>().object;
|
|
||||||
sinon.stub(azureFunctionUtils, 'waitForNewFunctionFile').withArgs(sinon.match.any).returns({ filePromise: Promise.resolve('TestFileCreated'), watcherDisposable: testWatcher });
|
|
||||||
|
|
||||||
should(connectionInfo.database).equal('my_db', 'ConnectionInfo database should not be changed');
|
|
||||||
await azureFunctionService.createAzureFunction();
|
|
||||||
|
|
||||||
should(showErrorMessageSpy.notCalled).be.true('showErrorMessage should not have been called');
|
|
||||||
// set the connection info to be the one the user selects from list of databases quickpick
|
|
||||||
should(connectionInfo.database).equal('testDb', 'connectionInfo.database should be testDb after user selects testDb');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should create azure function project using command via the sql server table OE', async function (): Promise<void> {
|
|
||||||
// This test will have an azure function project already in the project and the azure functions extension installed (stubbed)
|
|
||||||
sinon.stub(azureFunctionUtils, 'getAzureFunctionsExtensionApi').resolves(testUtils.azureFunctionsExtensionApi.object); // set azure functions extension api
|
|
||||||
sinon.stub(azureFunctionUtils, 'getAzureFunctionProject').resolves(projectFilePath); //set azure function project to have one project
|
|
||||||
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
|
||||||
|
|
||||||
// create fake connection string settings for local.setting.json to be used
|
|
||||||
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
|
|
||||||
sinon.stub(fs, 'readFileSync').withArgs(sinon.match.any).returns(
|
|
||||||
`{"IsEncrypted": false,
|
|
||||||
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
|
|
||||||
);
|
|
||||||
|
|
||||||
let connectionInfo: IConnectionInfo = createTestCredentials();// create test connectionInfo
|
|
||||||
let connectionDetails = { options: connectionInfo };
|
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
|
||||||
|
|
||||||
const showErrorMessageSpy = sinon.spy(vscode.window, 'showErrorMessage');
|
|
||||||
|
|
||||||
// select input or output binding
|
|
||||||
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves(<any>{ label: constants.input, type: BindingType.input });
|
|
||||||
// table node used when creating azure function project
|
|
||||||
let tableTestNode = createTestTableNode(connectionInfo);
|
|
||||||
|
|
||||||
// set azure function name
|
|
||||||
let inputStub = sinon.stub(vscode.window, 'showInputBox').resolves('testFunctionName');
|
|
||||||
|
|
||||||
// promptAndUpdateConnectionStringSetting
|
|
||||||
quickPickStub.onSecondCall().resolves(<any>{ label: constants.createNewLocalAppSettingWithIcon });
|
|
||||||
inputStub.onSecondCall().resolves('SqlConnectionString');
|
|
||||||
// promptConnectionStringPasswordAndUpdateConnectionString - tested in AzureFunctionUtils.test.ts
|
|
||||||
quickPickStub.onThirdCall().resolves((constants.yesString) as any);
|
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
|
||||||
// setLocalAppSetting with connection string setting name and connection string
|
|
||||||
// fails if we dont set writeFile stub
|
|
||||||
sinon.stub(fs.promises, 'writeFile').resolves();
|
|
||||||
sinon.stub(azureFunctionUtils, 'setLocalAppSetting').withArgs(sinon.match.any, 'SqlConnectionString', 'testConnectionString').resolves((true));
|
|
||||||
sinon.stub(utils, 'executeCommand').resolves('downloaded nuget package');
|
|
||||||
|
|
||||||
const testWatcher = TypeMoq.Mock.ofType<vscode.FileSystemWatcher>().object;
|
|
||||||
sinon.stub(azureFunctionUtils, 'waitForNewFunctionFile').withArgs(sinon.match.any).returns({ filePromise: Promise.resolve('TestFileCreated'), watcherDisposable: testWatcher });
|
|
||||||
|
|
||||||
should(connectionInfo.database).equal('my_db', 'ConnectionInfo database should not be changed');
|
|
||||||
await azureFunctionService.createAzureFunction(tableTestNode);
|
|
||||||
|
|
||||||
should(showErrorMessageSpy.notCalled).be.true('showErrorMessage should not have been called');
|
|
||||||
// set the connection info to be the one used from the test table node from OE
|
|
||||||
should(connectionInfo.database).equal('testDb', 'connectionInfo.database should be testDb after user selects testDb');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should open link to learn more about SQL bindings when no azure function project found in folder or workspace', async function (): Promise<void> {
|
it('Should open link to learn more about SQL bindings when no azure function project found in folder or workspace', async function (): Promise<void> {
|
||||||
// This test will ask user that an azure function project must be opened to create an azure function with sql binding
|
// This test will ask user that an azure function project must be opened to create an azure function with sql binding
|
||||||
sinon.stub(azureFunctionUtils, 'getAzureFunctionsExtensionApi').resolves(testUtils.azureFunctionsExtensionApi.object); // set azure functions extension api
|
sinon.stub(azureFunctionUtils, 'getAzureFunctionsExtensionApi').resolves(testUtils.azureFunctionsExtensionApi.object); // set azure functions extension api
|
||||||
@@ -205,11 +93,199 @@ describe('AzureFunctionsService', () => {
|
|||||||
const testWatcher = TypeMoq.Mock.ofType<vscode.FileSystemWatcher>().object;
|
const testWatcher = TypeMoq.Mock.ofType<vscode.FileSystemWatcher>().object;
|
||||||
sinon.stub(azureFunctionUtils, 'waitForNewFunctionFile').withArgs(sinon.match.any).returns({ filePromise: Promise.resolve('TestFileCreated'), watcherDisposable: testWatcher });
|
sinon.stub(azureFunctionUtils, 'waitForNewFunctionFile').withArgs(sinon.match.any).returns({ filePromise: Promise.resolve('TestFileCreated'), watcherDisposable: testWatcher });
|
||||||
|
|
||||||
should(connectionInfo.database).equal('my_db', 'ConnectionInfo database should not be changed');
|
should(connectionInfo.database).equal('my_db', 'Initial ConnectionInfo database should be my_db');
|
||||||
await azureFunctionService.createAzureFunction(tableTestNode);
|
await azureFunctionService.createAzureFunction(tableTestNode);
|
||||||
|
|
||||||
should(showErrorStub.calledOnce).be.true('showErrorMessage should have been called');
|
should(showErrorStub.calledOnce).be.true('showErrorMessage should have been called');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('For table object', () => {
|
||||||
|
let connectionInfo: IConnectionInfo;
|
||||||
|
let connectionDetails: ConnectionDetails;
|
||||||
|
let showErrorMessageSpy: sinon.SinonSpy;
|
||||||
|
|
||||||
|
beforeEach(function (): void {
|
||||||
|
// This test will have an azure function project already in the project and the azure functions extension installed (stubbed)
|
||||||
|
sinon.stub(azureFunctionUtils, 'getAzureFunctionsExtensionApi').resolves(testUtils.azureFunctionsExtensionApi.object); // set azure functions extension api
|
||||||
|
sinon.stub(azureFunctionUtils, 'getAzureFunctionProject').resolves(projectFilePath); //set azure function project to have one project
|
||||||
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
|
|
||||||
|
// create fake connection string settings for local.setting.json to be used
|
||||||
|
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
|
||||||
|
sinon.stub(fs, 'readFileSync').withArgs(sinon.match.any).returns(
|
||||||
|
`{"IsEncrypted": false,
|
||||||
|
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
|
||||||
|
);
|
||||||
|
|
||||||
|
connectionInfo = createTestCredentials(); // create test connectionInfo
|
||||||
|
connectionDetails = { options: connectionInfo }; // use for getConnectionString request
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|
||||||
|
// setLocalAppSetting with connection string setting name and connection string
|
||||||
|
// fails if we dont set writeFile stub
|
||||||
|
sinon.stub(fs.promises, 'writeFile').resolves();
|
||||||
|
sinon.stub(azureFunctionUtils, 'setLocalAppSetting').withArgs(sinon.match.any, 'SqlConnectionString', 'testConnectionString').resolves((true));
|
||||||
|
sinon.stub(utils, 'executeCommand').resolves('downloaded nuget package');
|
||||||
|
|
||||||
|
const testWatcher = TypeMoq.Mock.ofType<vscode.FileSystemWatcher>().object;
|
||||||
|
sinon.stub(azureFunctionUtils, 'waitForNewFunctionFile').withArgs(sinon.match.any).returns({ filePromise: Promise.resolve('TestFileCreated'), watcherDisposable: testWatcher });
|
||||||
|
|
||||||
|
showErrorMessageSpy = sinon.spy(vscode.window, 'showErrorMessage'); // error message spy to be used for checking tests
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should create azure function project using the command from command palette (no connection info)', async function (): Promise<void> {
|
||||||
|
// select table
|
||||||
|
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves({ label: constants.input, type: ObjectType.Table } as any);
|
||||||
|
// select input or output binding
|
||||||
|
quickPickStub.onSecondCall().resolves(<any>{ label: constants.input, type: BindingType.input });
|
||||||
|
|
||||||
|
// no connection node used for connection info so prompt user to get connection info
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.promptForConnection(true)).returns(() => Promise.resolve(connectionInfo));
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.connect(connectionInfo)).returns(() => Promise.resolve('testConnectionURI'));
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.listDatabases('testConnectionURI')).returns(() => Promise.resolve(['testDb']));
|
||||||
|
// select the testDB from list of databases based on connection info
|
||||||
|
quickPickStub.onThirdCall().resolves(('testDb') as any);
|
||||||
|
// get tables from selected database
|
||||||
|
const params = { ownerUri: 'testConnectionURI', queryString: azureFunctionUtils.tablesQuery('testDb') };
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.sendRequest(azureFunctionsContracts.SimpleExecuteRequest.type, params))
|
||||||
|
.returns(() => Promise.resolve({ rowCount: 1, columnInfo: [], rows: [[{ displayValue: '[schema].[testTable]' }]] }));
|
||||||
|
// select the schema.testTable from list of tables based on connection info and database
|
||||||
|
quickPickStub.onCall(3).resolves(('[schema].[testTable]') as any);
|
||||||
|
|
||||||
|
// set azure function name
|
||||||
|
let inputStub = sinon.stub(vscode.window, 'showInputBox').resolves('testFunctionName');
|
||||||
|
|
||||||
|
// promptAndUpdateConnectionStringSetting
|
||||||
|
quickPickStub.onCall(4).resolves(<any>{ label: constants.createNewLocalAppSettingWithIcon });
|
||||||
|
inputStub.onSecondCall().resolves('SqlConnectionString');
|
||||||
|
// promptConnectionStringPasswordAndUpdateConnectionString - tested in AzureFunctionUtils.test.ts
|
||||||
|
quickPickStub.onCall(5).resolves((constants.yesString) as any);
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|
||||||
|
should(connectionInfo.database).equal('my_db', 'Initial ConnectionInfo database should be my_db');
|
||||||
|
await azureFunctionService.createAzureFunction();
|
||||||
|
|
||||||
|
should(showErrorMessageSpy.notCalled).be.true('showErrorMessage should not have been called');
|
||||||
|
should(connectionInfo.database).equal('testDb', 'connectionInfo.database should be testDb after user selects testDb');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should create azure function project using command via the sql server table object explorer', async function (): Promise<void> {
|
||||||
|
// select input or output binding
|
||||||
|
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves(<any>{ label: constants.input, type: BindingType.input });
|
||||||
|
// table node used when creating azure function project
|
||||||
|
let tableTestNode = createTestTableNode(connectionInfo);
|
||||||
|
|
||||||
|
// set azure function name
|
||||||
|
let inputStub = sinon.stub(vscode.window, 'showInputBox').resolves('testFunctionName');
|
||||||
|
|
||||||
|
// promptAndUpdateConnectionStringSetting
|
||||||
|
quickPickStub.onSecondCall().resolves(<any>{ label: constants.createNewLocalAppSettingWithIcon });
|
||||||
|
inputStub.onSecondCall().resolves('SqlConnectionString');
|
||||||
|
// promptConnectionStringPasswordAndUpdateConnectionString - tested in AzureFunctionUtils.test.ts
|
||||||
|
quickPickStub.onThirdCall().resolves((constants.yesString) as any);
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|
||||||
|
should(connectionInfo.database).equal('my_db', 'Initial ConnectionInfo database should be my_db');
|
||||||
|
await azureFunctionService.createAzureFunction(tableTestNode);
|
||||||
|
|
||||||
|
should(showErrorMessageSpy.notCalled).be.true('showErrorMessage should not have been called');
|
||||||
|
should(connectionInfo.database).equal('testDb', 'connectionInfo.database should be testDb used from the test table node from OE');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('For view object', function (): void {
|
||||||
|
let connectionInfo: IConnectionInfo;
|
||||||
|
let connectionDetails: ConnectionDetails;
|
||||||
|
let showErrorMessageSpy: sinon.SinonSpy;
|
||||||
|
|
||||||
|
beforeEach(function (): void {
|
||||||
|
// This test will have an azure function project already in the project and the azure functions extension installed (stubbed)
|
||||||
|
sinon.stub(azureFunctionUtils, 'getAzureFunctionsExtensionApi').resolves(testUtils.azureFunctionsExtensionApi.object); // set azure functions extension api
|
||||||
|
sinon.stub(azureFunctionUtils, 'getAzureFunctionProject').resolves(projectFilePath); //set azure function project to have one project
|
||||||
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
|
|
||||||
|
// create fake connection string settings for local.setting.json to be used
|
||||||
|
sinon.stub(fs.promises, 'access').onFirstCall().resolves();
|
||||||
|
sinon.stub(fs, 'readFileSync').withArgs(sinon.match.any).returns(
|
||||||
|
`{"IsEncrypted": false,
|
||||||
|
"Values": {"test1": "test1", "test2": "test2", "test3":"test3"}}`
|
||||||
|
);
|
||||||
|
|
||||||
|
connectionInfo = createTestCredentials(); // create test connectionInfo
|
||||||
|
connectionDetails = { options: connectionInfo }; // use for getConnectionString request
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|
||||||
|
// setLocalAppSetting with connection string setting name and connection string
|
||||||
|
// fails if we dont set writeFile stub
|
||||||
|
sinon.stub(fs.promises, 'writeFile').resolves();
|
||||||
|
sinon.stub(azureFunctionUtils, 'setLocalAppSetting').withArgs(sinon.match.any, 'SqlConnectionString', 'testConnectionString').resolves((true));
|
||||||
|
sinon.stub(utils, 'executeCommand').resolves('downloaded nuget package');
|
||||||
|
|
||||||
|
const testWatcher = TypeMoq.Mock.ofType<vscode.FileSystemWatcher>().object;
|
||||||
|
sinon.stub(azureFunctionUtils, 'waitForNewFunctionFile').withArgs(sinon.match.any).returns({ filePromise: Promise.resolve('TestFileCreated'), watcherDisposable: testWatcher });
|
||||||
|
|
||||||
|
showErrorMessageSpy = sinon.spy(vscode.window, 'showErrorMessage'); // error message spy to be used for checking tests
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should create azure function project using the command from command palette (no connection info)', async function (): Promise<void> {
|
||||||
|
// select view
|
||||||
|
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves({ label: constants.input, type: ObjectType.View } as any);
|
||||||
|
// input binding should be used for views
|
||||||
|
|
||||||
|
// no connection node used for connection info so prompt user to get connection info
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.promptForConnection(true)).returns(() => Promise.resolve(connectionInfo));
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.connect(connectionInfo)).returns(() => Promise.resolve('testConnectionURI'));
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.listDatabases('testConnectionURI')).returns(() => Promise.resolve(['testDb']));
|
||||||
|
// select the testDB from list of databases based on connection info
|
||||||
|
quickPickStub.onSecondCall().resolves(('testDb') as any);
|
||||||
|
// get views from selected database
|
||||||
|
const params = { ownerUri: 'testConnectionURI', queryString: azureFunctionUtils.viewsQuery('testDb') };
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.sendRequest(azureFunctionsContracts.SimpleExecuteRequest.type, params))
|
||||||
|
.returns(() => Promise.resolve({ rowCount: 1, columnInfo: [], rows: [[{ displayValue: '[schema].[testView]' }]] }));
|
||||||
|
// select the schema.testView from list of tables based on connection info and database
|
||||||
|
quickPickStub.onThirdCall().resolves(('[schema].[testView]') as any);
|
||||||
|
|
||||||
|
// set azure function name
|
||||||
|
let inputStub = sinon.stub(vscode.window, 'showInputBox').resolves('testFunctionName');
|
||||||
|
|
||||||
|
// promptAndUpdateConnectionStringSetting
|
||||||
|
quickPickStub.onCall(3).resolves(<any>{ label: constants.createNewLocalAppSettingWithIcon });
|
||||||
|
inputStub.onSecondCall().resolves('SqlConnectionString');
|
||||||
|
// promptConnectionStringPasswordAndUpdateConnectionString - tested in AzureFunctionUtils.test.ts
|
||||||
|
quickPickStub.onCall(4).resolves((constants.yesString) as any);
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|
||||||
|
should(connectionInfo.database).equal('my_db', 'Initial ConnectionInfo database should be my_db');
|
||||||
|
await azureFunctionService.createAzureFunction();
|
||||||
|
|
||||||
|
should(showErrorMessageSpy.notCalled).be.true('showErrorMessage should not have been called');
|
||||||
|
should(connectionInfo.database).equal('testDb', 'connectionInfo.database should be testDb after user selects testDb');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should create azure function project using command via the sql server view object explorer', async function (): Promise<void> {
|
||||||
|
// Since views is stricly used only with input binding we go directly to asking for function name
|
||||||
|
// set azure function name
|
||||||
|
let inputStub = sinon.stub(vscode.window, 'showInputBox').resolves('testFunctionName');
|
||||||
|
|
||||||
|
// prompt user to select sql connection string setting from local.settings.json
|
||||||
|
let quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves(<any>{ label: constants.createNewLocalAppSettingWithIcon });
|
||||||
|
|
||||||
|
// promptAndUpdateConnectionStringSetting
|
||||||
|
inputStub.onSecondCall().resolves('SqlConnectionString');
|
||||||
|
// promptConnectionStringPasswordAndUpdateConnectionString - tested in AzureFunctionUtils.test.ts
|
||||||
|
quickPickStub.onSecondCall().resolves((constants.yesString) as any);
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.getConnectionString(connectionDetails, true, false)).returns(() => Promise.resolve('testConnectionString'));
|
||||||
|
|
||||||
|
should(connectionInfo.database).equal('my_db', 'Initial ConnectionInfo database should be my_db');
|
||||||
|
|
||||||
|
// table node used when creating azure function project
|
||||||
|
let tableTestNode = createTestTableNode(connectionInfo);
|
||||||
|
await azureFunctionService.createAzureFunction(tableTestNode);
|
||||||
|
|
||||||
|
should(showErrorMessageSpy.notCalled).be.true('showErrorMessage should not have been called');
|
||||||
|
should(connectionInfo.database).equal('testDb', 'connectionInfo.database should be testDb used from the test view node from OE');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Cancel/Error scenarios for Azure Function with SQL Binding ', function (): void {
|
describe('Cancel/Error scenarios for Azure Function with SQL Binding ', function (): void {
|
||||||
@@ -218,11 +294,12 @@ describe('AzureFunctionsService', () => {
|
|||||||
sinon.stub(azureFunctionUtils, 'getAzureFunctionsExtensionApi').resolves(testUtils.azureFunctionsExtensionApi.object); // set azure functions extension api
|
sinon.stub(azureFunctionUtils, 'getAzureFunctionsExtensionApi').resolves(testUtils.azureFunctionsExtensionApi.object); // set azure functions extension api
|
||||||
sinon.stub(azureFunctionUtils, 'getAzureFunctionProject').resolves(projectFilePath); //set azure function project to have one project
|
sinon.stub(azureFunctionUtils, 'getAzureFunctionProject').resolves(projectFilePath); //set azure function project to have one project
|
||||||
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
sinon.stub(utils, 'getVscodeMssqlApi').resolves(testUtils.vscodeMssqlIExtension.object);
|
||||||
|
|
||||||
// select input or output binding
|
|
||||||
quickPickStub = sinon.stub(vscode.window, 'showQuickPick').resolves(<any>{ label: constants.input, type: BindingType.input });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should prompt connection profile when user cancels selecting database', async function (): Promise<void> {
|
it('Should prompt connection profile when user cancels selecting database', async function (): Promise<void> {
|
||||||
|
// select view for object type
|
||||||
|
quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves({ label: constants.input, type: ObjectType.View } as any);
|
||||||
|
|
||||||
// This test will have an azure function project already in the project and the azure functions extension installed (stubbed)
|
// This test will have an azure function project already in the project and the azure functions extension installed (stubbed)
|
||||||
let connectionInfo: IConnectionInfo = createTestCredentials();// create test connectionInfo
|
let connectionInfo: IConnectionInfo = createTestCredentials();// create test connectionInfo
|
||||||
|
|
||||||
@@ -241,7 +318,10 @@ describe('AzureFunctionsService', () => {
|
|||||||
should(promptForConnectionStub.callCount).equal(2, 'promptForConnection should have been called 2 times only');
|
should(promptForConnectionStub.callCount).equal(2, 'promptForConnection should have been called 2 times only');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should prompt connection profile when user cancels selecting table', async function (): Promise<void> {
|
it('Should prompt connection profile when user cancels selecting object from object lists', async function (): Promise<void> {
|
||||||
|
// select view for object type
|
||||||
|
quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves({ label: constants.input, type: ObjectType.View } as any);
|
||||||
|
|
||||||
// This test will re-prompt the user to choose connection profile
|
// This test will re-prompt the user to choose connection profile
|
||||||
let connectionInfo: IConnectionInfo = createTestCredentials();// create test connectionInfo
|
let connectionInfo: IConnectionInfo = createTestCredentials();// create test connectionInfo
|
||||||
|
|
||||||
@@ -253,10 +333,10 @@ describe('AzureFunctionsService', () => {
|
|||||||
// select the testDB for promptForDatabase
|
// select the testDB for promptForDatabase
|
||||||
quickPickStub.onSecondCall().resolves(('testDb') as any);
|
quickPickStub.onSecondCall().resolves(('testDb') as any);
|
||||||
|
|
||||||
// get tables from selected database
|
// get views from selected database
|
||||||
const params = { ownerUri: 'testConnectionURI', queryString: azureFunctionUtils.tablesQuery('testDb') };
|
const params = { ownerUri: 'testConnectionURI', queryString: azureFunctionUtils.viewsQuery('testDb') };
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.sendRequest(azureFunctionsContracts.SimpleExecuteRequest.type, params))
|
testUtils.vscodeMssqlIExtension.setup(x => x.sendRequest(azureFunctionsContracts.SimpleExecuteRequest.type, params))
|
||||||
.returns(() => Promise.resolve({ rowCount: 1, columnInfo: [], rows: [[{ displayValue: '[schema].[testTable]' }]] }));
|
.returns(() => Promise.resolve({ rowCount: 1, columnInfo: [], rows: [[{ displayValue: '[schema].[testView]' }]] }));
|
||||||
|
|
||||||
// cancel out of promptForTables - select table to use
|
// cancel out of promptForTables - select table to use
|
||||||
quickPickStub.onThirdCall().resolves(undefined);
|
quickPickStub.onThirdCall().resolves(undefined);
|
||||||
@@ -270,31 +350,34 @@ describe('AzureFunctionsService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should prompt select table when user cancels out of manually entering table', async function (): Promise<void> {
|
it('Should prompt select table when user cancels out of manually entering table', async function (): Promise<void> {
|
||||||
|
// select table for object type
|
||||||
|
quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves({ label: constants.input, type: ObjectType.Table } as any);
|
||||||
|
quickPickStub.onSecondCall().resolves(<any>{ label: constants.input, type: BindingType.input });
|
||||||
// This test will have an azure function project already in the project and the azure functions extension installed (stubbed)
|
// This test will have an azure function project already in the project and the azure functions extension installed (stubbed)
|
||||||
let connectionInfo: IConnectionInfo = createTestCredentials();// create test connectionInfo
|
let connectionInfo: IConnectionInfo = createTestCredentials();// create test connectionInfo
|
||||||
|
|
||||||
// no table used for connection info so prompt user to get connection info
|
// no connection node used for connection info so prompt user to get connection info
|
||||||
// promptForConnection is set first time for user
|
// promptForConnection is set first time for user
|
||||||
let promptForConnectionStub = sinon.stub(testUtils.vscodeMssqlIExtension.object, 'promptForConnection').withArgs(true).onFirstCall().resolves(connectionInfo);
|
let promptForConnectionStub = sinon.stub(testUtils.vscodeMssqlIExtension.object, 'promptForConnection').withArgs(true).onFirstCall().resolves(connectionInfo);
|
||||||
// setup listDatabases request with connectionURI
|
// setup listDatabases request with connectionURI
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.connect(connectionInfo)).returns(() => Promise.resolve('testConnectionURI'));
|
testUtils.vscodeMssqlIExtension.setup(x => x.connect(connectionInfo)).returns(() => Promise.resolve('testConnectionURI'));
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.listDatabases('testConnectionURI')).returns(() => Promise.resolve(['testDb']));
|
testUtils.vscodeMssqlIExtension.setup(x => x.listDatabases('testConnectionURI')).returns(() => Promise.resolve(['testDb']));
|
||||||
// select the testDB from list of databases based on connection info
|
// select the testDB from list of databases based on connection info
|
||||||
quickPickStub.onSecondCall().resolves(('testDb') as any);
|
quickPickStub.onThirdCall().resolves(('testDb') as any);
|
||||||
// get tables from selected database
|
// get tables from selected database
|
||||||
const params = { ownerUri: 'testConnectionURI', queryString: azureFunctionUtils.tablesQuery('testDb') };
|
const params = { ownerUri: 'testConnectionURI', queryString: azureFunctionUtils.tablesQuery('testDb') };
|
||||||
testUtils.vscodeMssqlIExtension.setup(x => x.sendRequest(azureFunctionsContracts.SimpleExecuteRequest.type, params))
|
testUtils.vscodeMssqlIExtension.setup(x => x.sendRequest(azureFunctionsContracts.SimpleExecuteRequest.type, params))
|
||||||
.returns(() => Promise.resolve({ rowCount: 1, columnInfo: [], rows: [[{ displayValue: '[schema].[testTable]' }]] }));
|
.returns(() => Promise.resolve({ rowCount: 1, columnInfo: [], rows: [[{ displayValue: '[schema].[testTable]' }]] }));
|
||||||
// select the option to manually enter table name
|
// select the option to manually enter table name
|
||||||
let manuallyEnterObjectName = constants.manuallyEnterObjectName(constants.enterObjectName);
|
let manuallyEnterObjectName = constants.manuallyEnterObjectName(constants.enterTableName);
|
||||||
quickPickStub.onThirdCall().resolves(manuallyEnterObjectName as any);
|
quickPickStub.onCall(3).resolves(manuallyEnterObjectName as any);
|
||||||
// cancel out of manually enter inputBox
|
// cancel out of manually enter inputBox
|
||||||
sinon.stub(vscode.window, 'showInputBox').resolves(undefined);
|
sinon.stub(vscode.window, 'showInputBox').resolves(undefined);
|
||||||
// resolve promises to undefined to exit out of createFunction
|
// resolve promises to undefined to exit out of createFunction
|
||||||
quickPickStub.onCall(4).resolves(undefined);
|
quickPickStub.onCall(4).resolves(undefined);
|
||||||
promptForConnectionStub.onSecondCall().resolves(undefined);
|
promptForConnectionStub.onSecondCall().resolves(undefined);
|
||||||
|
|
||||||
should(connectionInfo.database).equal('my_db', 'ConnectionInfo database should not be changed');
|
should(connectionInfo.database).equal('my_db', 'Initial ConnectionInfo database should be my_db');
|
||||||
await azureFunctionService.createAzureFunction();
|
await azureFunctionService.createAzureFunction();
|
||||||
|
|
||||||
should(connectionInfo.database).equal('testDb', 'ConnectionInfo database should be user selected database');
|
should(connectionInfo.database).equal('testDb', 'ConnectionInfo database should be user selected database');
|
||||||
@@ -308,8 +391,52 @@ describe('AzureFunctionsService', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Should prompt select view when user cancels out of manually entering view', async function (): Promise<void> {
|
||||||
|
// select view for object type
|
||||||
|
quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves({ label: constants.input, type: ObjectType.View } as any);
|
||||||
|
// This test will have an azure function project already in the project and the azure functions extension installed (stubbed)
|
||||||
|
let connectionInfo: IConnectionInfo = createTestCredentials();// create test connectionInfo
|
||||||
|
|
||||||
|
// no connection node used for connection info so prompt user to get connection info
|
||||||
|
// promptForConnection is set first time for user
|
||||||
|
let promptForConnectionStub = sinon.stub(testUtils.vscodeMssqlIExtension.object, 'promptForConnection').withArgs(true).onFirstCall().resolves(connectionInfo);
|
||||||
|
// setup listDatabases request with connectionURI
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.connect(connectionInfo)).returns(() => Promise.resolve('testConnectionURI'));
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.listDatabases('testConnectionURI')).returns(() => Promise.resolve(['testDb']));
|
||||||
|
// select the testDB from list of databases based on connection info
|
||||||
|
quickPickStub.onSecondCall().resolves(('testDb') as any);
|
||||||
|
// get tables from selected database
|
||||||
|
const params = { ownerUri: 'testConnectionURI', queryString: azureFunctionUtils.viewsQuery('testDb') };
|
||||||
|
testUtils.vscodeMssqlIExtension.setup(x => x.sendRequest(azureFunctionsContracts.SimpleExecuteRequest.type, params))
|
||||||
|
.returns(() => Promise.resolve({ rowCount: 1, columnInfo: [], rows: [[{ displayValue: '[schema].[testView]' }]] }));
|
||||||
|
// select the option to manually enter table name
|
||||||
|
let manuallyEnterObjectName = constants.manuallyEnterObjectName(constants.enterViewName);
|
||||||
|
quickPickStub.onThirdCall().resolves(manuallyEnterObjectName as any);
|
||||||
|
// cancel out of manually enter inputBox
|
||||||
|
sinon.stub(vscode.window, 'showInputBox').resolves(undefined);
|
||||||
|
// resolve promises to undefined to exit out of createFunction
|
||||||
|
quickPickStub.onCall(3).resolves(undefined);
|
||||||
|
promptForConnectionStub.onSecondCall().resolves(undefined);
|
||||||
|
|
||||||
|
should(connectionInfo.database).equal('my_db', 'Initial ConnectionInfo database should be my_db');
|
||||||
|
await azureFunctionService.createAzureFunction();
|
||||||
|
|
||||||
|
should(connectionInfo.database).equal('testDb', 'ConnectionInfo database should be user selected database');
|
||||||
|
should(quickPickStub.getCall(3).args).containDeepOrdered([
|
||||||
|
[manuallyEnterObjectName, '[schema].[testView]'],
|
||||||
|
{
|
||||||
|
canPickMany: false,
|
||||||
|
title: constants.selectView,
|
||||||
|
ignoreFocusOut: true
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('Should prompt for connection profile if connection throws connection error', async function (): Promise<void> {
|
it('Should prompt for connection profile if connection throws connection error', async function (): Promise<void> {
|
||||||
// no table used for connection info so prompt user to get connection info
|
// select view for object type
|
||||||
|
quickPickStub = sinon.stub(vscode.window, 'showQuickPick').onFirstCall().resolves({ label: constants.input, type: ObjectType.View } as any);
|
||||||
|
|
||||||
|
// no connection node used for connection info so prompt user to get connection info
|
||||||
// promptForConnection is selected first time for user and then set undefined in order to exit out of the createFunction
|
// promptForConnection is selected first time for user and then set undefined in order to exit out of the createFunction
|
||||||
let promptForConnectionStub = sinon.stub(testUtils.vscodeMssqlIExtension.object, 'promptForConnection').withArgs(true).throws('Error connecting to connection profile');
|
let promptForConnectionStub = sinon.stub(testUtils.vscodeMssqlIExtension.object, 'promptForConnection').withArgs(true).throws('Error connecting to connection profile');
|
||||||
promptForConnectionStub.onSecondCall().resolves(undefined);
|
promptForConnectionStub.onSecondCall().resolves(undefined);
|
||||||
|
|||||||
Reference in New Issue
Block a user