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:
Vasu Bhog
2022-07-06 10:31:00 -07:00
committed by GitHub
parent 2e2fcbe5fc
commit 163ca5ec64
8 changed files with 378 additions and 185 deletions

View File

@@ -8,7 +8,7 @@ import * as path from 'path';
import * as utils from './utils';
import * as constants from './constants';
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';
// https://github.com/microsoft/vscode-azurefunctions/blob/main/src/vscode-azurefunctions.api.d.ts
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
* @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
* @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 })[] = [
{
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,
title: constants.selectBindingType(funcName),
ignoreFocusOut: true
@@ -311,14 +316,30 @@ export async function promptForBindingType(funcName?: string): Promise<BindingTy
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
* @param bindingType Type of SQL Binding
* @param connectionInfo (optional) connection info from the selected connection profile
* 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
*/
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)
let connectionURI: string | undefined;
let selectedDatabase: string | undefined;
@@ -329,9 +350,8 @@ export async function promptForObjectName(bindingType: BindingType, connectionIn
return promptToManuallyEnterObjectName(bindingType);
}
// TODO create path to solve for selectView (first need to support views as well)
// Prompt user to select a table based on connection profile and selected database}
// get connectionURI and selectedDatabase to be used for listing tables query request
// Prompt user to select a table/view based on connection profile and selected database
// get connectionURI and selectedDatabase to be used for listing tables/view query request
connectionURI = await getConnectionURI(connectionInfo);
if (!connectionURI) {
// mssql connection error
@@ -345,7 +365,7 @@ export async function promptForObjectName(bindingType: BindingType, connectionIn
connectionInfo.database = selectedDatabase;
let selectedObjectName = await promptSelectTable(connectionURI, bindingType, selectedDatabase);
let selectedObjectName = await promptSelectObject(connectionURI, bindingType, selectedDatabase, objectType);
return selectedObjectName;
}
@@ -618,35 +638,38 @@ export async function getConnectionURI(connectionInfo: IConnectionInfo): Promise
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 userObjectName = bindingType === BindingType.input ? constants.enterObjectName : constants.enterObjectNameToUpsert;
let isView = (objectType === ObjectType.View);
// Create query to get list of tables from database selected
let tableQuery = tablesQuery(selectedDatabase);
const params = { ownerUri: connectionURI, queryString: tableQuery };
const userObjectName = isView ? constants.enterViewName : bindingType === BindingType.input ? constants.enterTableName : constants.enterTableNameToUpsert;
// 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;
// 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(
{
location: vscode.ProgressLocation.Notification,
title: constants.tableListProgressTitle,
title: isView ? constants.viewListProgressTitle : constants.tableListProgressTitle,
cancellable: false
}, async (_progress, _token) => {
queryResult = await vscodeMssqlApi.sendRequest(azureFunctionsContracts.SimpleExecuteRequest.type, params);
}
);
// Get schema and table names from query result rows
const tableNames = 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)
// Get schema and table/view names from query result rows
const objectNames = queryResult!.rows.map(r => r[0].displayValue);
// add manual entry option to object names list for user to choose from as well (with pencil icon)
let manuallyEnterObjectName = constants.manuallyEnterObjectName(userObjectName);
tableNames.unshift(manuallyEnterObjectName);
// prompt user to select table from list of tables options
objectNames.unshift(manuallyEnterObjectName);
// prompt user to select object from list of objects
while (true) {
let selectedObject = await vscode.window.showQuickPick(tableNames, {
let selectedObject = await vscode.window.showQuickPick(objectNames, {
canPickMany: false,
title: constants.selectTable,
title: isView ? constants.selectView : constants.selectTable,
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'`;
}
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> {
// user manually enters table or view to query or upsert into
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,
validateInput: input => input ? undefined : constants.nameMustNotBeEmpty,
ignoreFocusOut: true