mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-04 17:23:45 -05:00
Make Create Azure Function with SQL Binding more efficient and simple (#19187)
* initial refactor * fix projectFolder cases * update create azure function with sql binding when no folder is opened * corner case exit browse file * add version since targetFramework is set to specific core tools version * update telemetry and address comments * use project folder instead
This commit is contained in:
@@ -10,7 +10,7 @@ import * as utils from '../common/utils';
|
||||
import * as azureFunctionsUtils from '../common/azureFunctionsUtils';
|
||||
import * as constants from '../common/constants';
|
||||
import * as azureFunctionsContracts from '../contracts/azureFunctions/azureFunctionsContracts';
|
||||
import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry';
|
||||
import { CreateAzureFunctionStep, TelemetryActions, TelemetryReporter, TelemetryViews, ExitReason } from '../common/telemetry';
|
||||
import { AddSqlBindingParams, BindingType, GetAzureFunctionsParams, GetAzureFunctionsResult, ResultStatus } from 'sql-bindings';
|
||||
import { IConnectionInfo, ITreeNodeInfo } from 'vscode-mssql';
|
||||
|
||||
@@ -20,285 +20,251 @@ export async function createAzureFunction(node?: ITreeNodeInfo): Promise<void> {
|
||||
// telemetry properties for create azure function
|
||||
let sessionId: string = uuid.v4();
|
||||
let propertyBag: { [key: string]: string } = { sessionId: sessionId };
|
||||
let quickPickStep: string = '';
|
||||
let exitReason: string = 'cancelled';
|
||||
let telemetryStep: string = '';
|
||||
let exitReason: string = ExitReason.cancelled;
|
||||
TelemetryReporter.sendActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.startCreateAzureFunctionWithSqlBinding);
|
||||
|
||||
let selectedBindingType: BindingType | undefined;
|
||||
let connectionInfo: IConnectionInfo | undefined;
|
||||
let connectionURI: string;
|
||||
let listDatabases: string[] | undefined;
|
||||
let objectName: string | undefined;
|
||||
const vscodeMssqlApi = await utils.getVscodeMssqlApi();
|
||||
if (!node) {
|
||||
// if user selects command in command palette we prompt user for information
|
||||
quickPickStep = 'launchFromCommandPalette';
|
||||
try {
|
||||
// Ask binding type for promptObjectName
|
||||
quickPickStep = 'getBindingType';
|
||||
let selectedBinding = await azureFunctionsUtils.promptForBindingType();
|
||||
if (!selectedBinding) {
|
||||
return;
|
||||
}
|
||||
selectedBindingType = selectedBinding;
|
||||
propertyBag.bindingType = selectedBindingType;
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.startCreateAzureFunctionWithSqlBinding)
|
||||
let isCreateNewProject: boolean = false;
|
||||
let newFunctionFileObject: azureFunctionsUtils.IFileFunctionObject | undefined;
|
||||
|
||||
try {
|
||||
const azureFunctionApi = await azureFunctionsUtils.getAzureFunctionsExtensionApi();
|
||||
if (!azureFunctionApi) {
|
||||
exitReason = ExitReason.error;
|
||||
propertyBag.exitReason = exitReason;
|
||||
TelemetryReporter.createErrorEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.exitCreateAzureFunctionQuickpick)
|
||||
.withAdditionalProperties(propertyBag).send();
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Prompts user for azure function project path to use
|
||||
* If multiple found in workspace we prompt user to pick one
|
||||
* If one found in workspace we use that
|
||||
* If none found in workspace we show error message but continue with createFunction message
|
||||
*/
|
||||
let projectFile = await azureFunctionsUtils.getAzureFunctionProject();
|
||||
let projectFolder: string;
|
||||
telemetryStep = CreateAzureFunctionStep.getAzureFunctionProject;
|
||||
if (!projectFile) {
|
||||
while (true) {
|
||||
// show warning message that user needs an azure function project to create a function
|
||||
let projectCreate = await vscode.window.showErrorMessage(constants.azureFunctionsProjectMustBeOpened,
|
||||
constants.createProject, constants.learnMore);
|
||||
if (projectCreate === constants.learnMore) {
|
||||
telemetryStep = CreateAzureFunctionStep.learnMore;
|
||||
exitReason = ExitReason.exit;
|
||||
void vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(constants.sqlBindingsDoc));
|
||||
return;
|
||||
} else if (projectCreate === constants.createProject) {
|
||||
telemetryStep = CreateAzureFunctionStep.helpCreateAzureFunctionProject;
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, telemetryStep)
|
||||
.withAdditionalProperties(propertyBag).send();
|
||||
|
||||
isCreateNewProject = true;
|
||||
telemetryStep = CreateAzureFunctionStep.getSelectedFolder;
|
||||
// user either has not folder open or an empty workspace
|
||||
// prompt user to choose a folder to create the project in
|
||||
const browseProjectLocation = await vscode.window.showQuickPick(
|
||||
[constants.browseEllipsisWithIcon],
|
||||
{ title: constants.selectAzureFunctionProjFolder, ignoreFocusOut: true });
|
||||
if (!browseProjectLocation) {
|
||||
// User cancelled
|
||||
return;
|
||||
}
|
||||
const projectFolders = (await vscode.window.showOpenDialog({
|
||||
canSelectFiles: false,
|
||||
canSelectFolders: true,
|
||||
canSelectMany: false,
|
||||
openLabel: constants.selectButton
|
||||
}));
|
||||
if (!projectFolders) {
|
||||
// User cancelled
|
||||
return;
|
||||
}
|
||||
projectFolder = projectFolders[0].fsPath;
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, telemetryStep)
|
||||
.withAdditionalProperties(propertyBag).send();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// user has an azure function project open
|
||||
projectFolder = path.dirname(projectFile);
|
||||
}
|
||||
// create a system file watcher for the project folder
|
||||
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
|
||||
let objectName: string | undefined;
|
||||
const vscodeMssqlApi = await utils.getVscodeMssqlApi();
|
||||
if (!node) {
|
||||
// if user selects command in command palette we prompt user for information
|
||||
telemetryStep = CreateAzureFunctionStep.launchFromCommandPalette;
|
||||
|
||||
// prompt user for connection profile to get connection info
|
||||
quickPickStep = 'getConnectionInfo';
|
||||
telemetryStep = CreateAzureFunctionStep.getConnectionProfile;
|
||||
connectionInfo = await vscodeMssqlApi.promptForConnection(true);
|
||||
if (!connectionInfo) {
|
||||
// User cancelled
|
||||
return;
|
||||
}
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.startCreateAzureFunctionWithSqlBinding)
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, telemetryStep)
|
||||
.withAdditionalProperties(propertyBag).withConnectionInfo(connectionInfo).send();
|
||||
|
||||
// list databases based on connection profile selected
|
||||
connectionURI = await vscodeMssqlApi.connect(connectionInfo);
|
||||
listDatabases = await vscodeMssqlApi.listDatabases(connectionURI);
|
||||
const selectedDatabase = (await vscode.window.showQuickPick(listDatabases, {
|
||||
canPickMany: false,
|
||||
title: constants.selectDatabase,
|
||||
ignoreFocusOut: true
|
||||
}));
|
||||
|
||||
telemetryStep = CreateAzureFunctionStep.getDatabase;
|
||||
let selectedDatabase = await azureFunctionsUtils.promptSelectDatabase(connectionInfo);
|
||||
if (!selectedDatabase) {
|
||||
// User cancelled
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
connectionInfo.database = selectedDatabase;
|
||||
|
||||
// prompt user for object name to create function from
|
||||
telemetryStep = CreateAzureFunctionStep.getObjectName;
|
||||
objectName = await azureFunctionsUtils.promptForObjectName(selectedBinding);
|
||||
if (!objectName) {
|
||||
// user cancelled
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
propertyBag.quickPickStep = quickPickStep;
|
||||
exitReason = 'error';
|
||||
TelemetryReporter.createErrorEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.exitCreateAzureFunctionQuickpick, undefined, utils.getErrorType(e))
|
||||
.withAdditionalProperties(propertyBag).send();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
quickPickStep = 'launchFromTable';
|
||||
connectionInfo = node.connectionInfo;
|
||||
// set the database containing the selected table so it can be used
|
||||
// for the initial catalog property of the connection string
|
||||
let newNode: ITreeNodeInfo = node;
|
||||
while (newNode) {
|
||||
if (newNode.nodeType === 'Database') {
|
||||
connectionInfo.database = newNode.metadata.name;
|
||||
break;
|
||||
} else {
|
||||
newNode = newNode.parentNode;
|
||||
} else {
|
||||
// if user selects table in tree view we use connection info from Object Explorer node
|
||||
telemetryStep = CreateAzureFunctionStep.launchFromTable;
|
||||
connectionInfo = node.connectionInfo;
|
||||
// set the database containing the selected table so it can be used
|
||||
// for the initial catalog property of the connection string
|
||||
let newNode: ITreeNodeInfo = node;
|
||||
while (newNode) {
|
||||
if (newNode.nodeType === 'Database') {
|
||||
connectionInfo.database = newNode.metadata.name;
|
||||
break;
|
||||
} else {
|
||||
newNode = newNode.parentNode;
|
||||
}
|
||||
}
|
||||
objectName = utils.generateQuotedFullName(node.metadata.schema, node.metadata.name);
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, telemetryStep)
|
||||
.withAdditionalProperties(propertyBag).withConnectionInfo(connectionInfo).send();
|
||||
}
|
||||
// Ask binding type for promptObjectName
|
||||
quickPickStep = 'getBindingType';
|
||||
let selectedBinding = await azureFunctionsUtils.promptForBindingType();
|
||||
|
||||
if (!selectedBinding) {
|
||||
// User cancelled
|
||||
// get function name from user
|
||||
telemetryStep = CreateAzureFunctionStep.getAzureFunctionName;
|
||||
let functionName: string;
|
||||
// remove special characters from function name
|
||||
let uniqueObjectName = utils.santizeObjectName(objectName);
|
||||
let uniqueFunctionName = await utils.getUniqueFileName(uniqueObjectName, projectFolder);
|
||||
functionName = await vscode.window.showInputBox({
|
||||
title: constants.functionNameTitle,
|
||||
value: uniqueFunctionName,
|
||||
ignoreFocusOut: true,
|
||||
validateInput: input => utils.validateFunctionName(input)
|
||||
}) as string;
|
||||
if (!functionName) {
|
||||
return;
|
||||
}
|
||||
selectedBindingType = selectedBinding;
|
||||
propertyBag.bindingType = selectedBinding;
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.startCreateAzureFunctionWithSqlBinding)
|
||||
.withAdditionalProperties(propertyBag).withConnectionInfo(connectionInfo).send();
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, telemetryStep)
|
||||
.withAdditionalProperties(propertyBag)
|
||||
.withConnectionInfo(connectionInfo).send();
|
||||
|
||||
objectName = utils.generateQuotedFullName(node.metadata.schema, node.metadata.name);
|
||||
}
|
||||
// set the templateId based on the selected binding type
|
||||
telemetryStep = CreateAzureFunctionStep.getTemplateId;
|
||||
let templateId: string = selectedBindingType === BindingType.input ? constants.inputTemplateID : constants.outputTemplateID;
|
||||
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.startCreateAzureFunctionWithSqlBinding)
|
||||
.withConnectionInfo(connectionInfo).send();
|
||||
quickPickStep = 'getAzureFunctionsExtensionApi';
|
||||
const azureFunctionApi = await azureFunctionsUtils.getAzureFunctionsExtensionApi();
|
||||
if (!azureFunctionApi) {
|
||||
propertyBag.exitReason = exitReason;
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.exitCreateAzureFunctionQuickpick)
|
||||
.withConnectionInfo(connectionInfo)
|
||||
// We need to set the azureWebJobsStorage to a placeholder
|
||||
// to suppress the warning for opening the wizard - but will ask them to overwrite if they are creating new azureFunction
|
||||
// issue https://github.com/microsoft/azuredatastudio/issues/18780
|
||||
telemetryStep = CreateAzureFunctionStep.setAzureWebJobsStorage;
|
||||
await azureFunctionsUtils.setLocalAppSetting(projectFolder, constants.azureWebJobsStorageSetting, constants.azureWebJobsStoragePlaceholder);
|
||||
|
||||
// prompt for Connection String Setting Name
|
||||
let connectionStringSettingName: string | undefined = constants.sqlConnectionStringSetting;
|
||||
if (!isCreateNewProject && projectFile) {
|
||||
telemetryStep = CreateAzureFunctionStep.getConnectionStringSettingName;
|
||||
connectionStringSettingName = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(vscode.Uri.parse(projectFile), connectionInfo);
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, telemetryStep)
|
||||
.withAdditionalProperties(propertyBag)
|
||||
.withConnectionInfo(connectionInfo).send();
|
||||
}
|
||||
|
||||
// create C# Azure Function with SQL Binding
|
||||
telemetryStep = 'createFunctionAPI';
|
||||
await azureFunctionApi.createFunction({
|
||||
language: 'C#',
|
||||
targetFramework: 'netcoreapp3.1',
|
||||
version: '~3',
|
||||
templateId: templateId,
|
||||
functionName: functionName,
|
||||
functionSettings: {
|
||||
connectionStringSetting: connectionStringSettingName,
|
||||
...(selectedBindingType === BindingType.input && { object: objectName }),
|
||||
...(selectedBindingType === BindingType.output && { table: objectName })
|
||||
},
|
||||
folderPath: projectFolder,
|
||||
suppressCreateProjectPrompt: true
|
||||
});
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, telemetryStep)
|
||||
.withAdditionalProperties(propertyBag)
|
||||
.withConnectionInfo(connectionInfo).send();
|
||||
|
||||
// check for the new function file to be created and dispose of the file system watcher
|
||||
const timeoutForFunctionFile = utils.timeoutPromise(constants.timeoutAzureFunctionFileError);
|
||||
let functionFilePath = await Promise.race([newFunctionFileObject.filePromise, timeoutForFunctionFile]);
|
||||
|
||||
// prompt user for include password for connection string
|
||||
if (isCreateNewProject && functionFilePath) {
|
||||
telemetryStep = CreateAzureFunctionStep.promptForIncludePassword;
|
||||
let settingsFile = await azureFunctionsUtils.getSettingsFile(projectFolder);
|
||||
if (!settingsFile) {
|
||||
return;
|
||||
}
|
||||
let connectionString = await azureFunctionsUtils.promptConnectionStringPasswordAndUpdateConnectionString(connectionInfo, settingsFile);
|
||||
if (!connectionString) {
|
||||
return;
|
||||
}
|
||||
void azureFunctionsUtils.addConnectionStringToConfig(connectionString, projectFolder, connectionStringSettingName);
|
||||
}
|
||||
|
||||
propertyBag.telemetryStep = telemetryStep;
|
||||
exitReason = ExitReason.finishCreate;
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.finishCreateAzureFunctionWithSqlBinding)
|
||||
.withAdditionalProperties(propertyBag)
|
||||
.withConnectionInfo(connectionInfo).send();
|
||||
} catch (error) {
|
||||
let errorType = utils.getErrorType(error);
|
||||
propertyBag.telemetryStep = telemetryStep;
|
||||
if (errorType === 'TimeoutError') {
|
||||
// this error can be cause by many different scenarios including timeout or error occurred during createFunction
|
||||
exitReason = ExitReason.timeout;
|
||||
console.log('Timed out waiting for Azure Function project to be created. This may not necessarily be an error, for example if the user canceled out of the create flow.');
|
||||
} else {
|
||||
// else an error would occur during the createFunction
|
||||
exitReason = ExitReason.error;
|
||||
void vscode.window.showErrorMessage(constants.errorNewAzureFunction(error));
|
||||
}
|
||||
TelemetryReporter.createErrorEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.exitCreateAzureFunctionQuickpick, undefined, errorType)
|
||||
.withAdditionalProperties(propertyBag).send();
|
||||
return;
|
||||
|
||||
}
|
||||
let projectFile = await azureFunctionsUtils.getAzureFunctionProject();
|
||||
let newHostProjectFile!: azureFunctionsUtils.IFileFunctionObject;
|
||||
let hostFile: string;
|
||||
|
||||
if (!projectFile) {
|
||||
let projectCreate = await vscode.window.showErrorMessage(constants.azureFunctionsProjectMustBeOpened,
|
||||
constants.createProject, constants.learnMore);
|
||||
if (projectCreate === constants.learnMore) {
|
||||
quickPickStep = 'learnMore';
|
||||
void vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(constants.sqlBindingsDoc));
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.learnMore)
|
||||
.withConnectionInfo(connectionInfo)
|
||||
.withAdditionalProperties(propertyBag).send();
|
||||
return;
|
||||
} else if (projectCreate === constants.createProject) {
|
||||
quickPickStep = 'helpCreateAzureFunctionProject';
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.helpCreateAzureFunctionProject)
|
||||
.withConnectionInfo(connectionInfo)
|
||||
.withAdditionalProperties(propertyBag).send();
|
||||
|
||||
// start the create azure function project flow
|
||||
try {
|
||||
// First prompt user for project location. We need to do this ourselves due to an issue
|
||||
// in the AF extension : https://github.com/microsoft/vscode-azurefunctions/issues/3115
|
||||
const browseProjectLocation = await vscode.window.showQuickPick(
|
||||
[constants.browseEllipsisWithIcon],
|
||||
{ title: constants.selectAzureFunctionProjFolder, ignoreFocusOut: true });
|
||||
if (!browseProjectLocation) {
|
||||
// User cancelled
|
||||
return undefined;
|
||||
}
|
||||
const projectFolders = (await vscode.window.showOpenDialog({
|
||||
canSelectFiles: false,
|
||||
canSelectFolders: true,
|
||||
canSelectMany: false,
|
||||
openLabel: constants.selectButton
|
||||
}));
|
||||
if (!projectFolders) {
|
||||
// User cancelled
|
||||
return;
|
||||
}
|
||||
const templateId: string = selectedBindingType === BindingType.input ? constants.inputTemplateID : constants.outputTemplateID;
|
||||
// because of an AF extension API issue, we have to get the newly created file by adding a watcher
|
||||
// issue: https://github.com/microsoft/vscode-azurefunctions/issues/3052
|
||||
newHostProjectFile = azureFunctionsUtils.waitForNewHostFile();
|
||||
await azureFunctionApi.createFunction({
|
||||
language: 'C#',
|
||||
targetFramework: 'netcoreapp3.1',
|
||||
templateId: templateId,
|
||||
suppressCreateProjectPrompt: true,
|
||||
folderPath: projectFolders[0].fsPath,
|
||||
functionSettings: {
|
||||
...(selectedBindingType === BindingType.input && { object: objectName }),
|
||||
...(selectedBindingType === BindingType.output && { table: objectName })
|
||||
},
|
||||
});
|
||||
const timeoutForHostFile = utils.timeoutPromise(constants.timeoutProjectError);
|
||||
hostFile = await Promise.race([newHostProjectFile.filePromise, timeoutForHostFile]);
|
||||
if (hostFile) {
|
||||
// start the add sql binding flow
|
||||
projectFile = await azureFunctionsUtils.getAzureFunctionProject();
|
||||
}
|
||||
} catch (error) {
|
||||
let errorType = utils.getErrorType(error);
|
||||
propertyBag.quickPickStep = quickPickStep;
|
||||
|
||||
if (errorType === 'TimeoutError') {
|
||||
// this error can be cause by many different scenarios including timeout or error occurred during createFunction
|
||||
exitReason = 'timeout';
|
||||
console.log('Timed out waiting for Azure Function project to be created. This may not necessarily be an error, for example if the user canceled out of the create flow.');
|
||||
} else {
|
||||
// else an error would occur during the createFunction
|
||||
exitReason = 'error';
|
||||
void vscode.window.showErrorMessage(constants.errorNewAzureFunction(error));
|
||||
}
|
||||
TelemetryReporter.createErrorEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.exitCreateAzureFunctionQuickpick, undefined, errorType)
|
||||
.withAdditionalProperties(propertyBag).send();
|
||||
return;
|
||||
} finally {
|
||||
propertyBag.exitReason = exitReason;
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.exitCreateAzureFunctionQuickpick)
|
||||
.withConnectionInfo(connectionInfo)
|
||||
.withAdditionalProperties(propertyBag).send();
|
||||
newHostProjectFile.watcherDisposable.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (projectFile) {
|
||||
// because of an AF extension API issue, we have to get the newly created file by adding a watcher
|
||||
// issue: https://github.com/microsoft/vscode-azurefunctions/issues/2908
|
||||
const newFunctionFileObject = azureFunctionsUtils.waitForNewFunctionFile(projectFile);
|
||||
let functionName: string;
|
||||
|
||||
try {
|
||||
// get function name from user
|
||||
quickPickStep = 'getAzureFunctionName';
|
||||
// remove special characters from function name
|
||||
let uniqueObjectName = utils.santizeObjectName(objectName);
|
||||
let uniqueFunctionName = await utils.getUniqueFileName(path.dirname(projectFile), uniqueObjectName);
|
||||
functionName = await vscode.window.showInputBox({
|
||||
title: constants.functionNameTitle,
|
||||
value: uniqueFunctionName,
|
||||
ignoreFocusOut: true,
|
||||
validateInput: input => utils.validateFunctionName(input)
|
||||
}) as string;
|
||||
if (!functionName) {
|
||||
return;
|
||||
}
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.getAzureFunctionProject)
|
||||
.withAdditionalProperties(propertyBag)
|
||||
.withConnectionInfo(connectionInfo).send();
|
||||
|
||||
// set the templateId based on the selected binding type
|
||||
let templateId: string = selectedBindingType === BindingType.input ? constants.inputTemplateID : constants.outputTemplateID;
|
||||
|
||||
// We need to set the azureWebJobsStorage to a placeholder
|
||||
// to suppress the warning for opening the wizard
|
||||
// issue https://github.com/microsoft/azuredatastudio/issues/18780
|
||||
await azureFunctionsUtils.setLocalAppSetting(path.dirname(projectFile), constants.azureWebJobsStorageSetting, constants.azureWebJobsStoragePlaceholder);
|
||||
|
||||
// prompt for connection string setting name and set connection string in local.settings.json
|
||||
quickPickStep = 'getConnectionStringSettingName';
|
||||
let connectionStringSettingName = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(vscode.Uri.parse(projectFile), connectionInfo);
|
||||
|
||||
// create C# Azure Function with SQL Binding
|
||||
await azureFunctionApi.createFunction({
|
||||
language: 'C#',
|
||||
templateId: templateId,
|
||||
functionName: functionName,
|
||||
targetFramework: 'netcoreapp3.1',
|
||||
functionSettings: {
|
||||
connectionStringSetting: connectionStringSettingName,
|
||||
...(selectedBindingType === BindingType.input && { object: objectName }),
|
||||
...(selectedBindingType === BindingType.output && { table: objectName })
|
||||
},
|
||||
folderPath: projectFile
|
||||
});
|
||||
|
||||
// check for the new function file to be created and dispose of the file system watcher
|
||||
const timeoutForFunctionFile = utils.timeoutPromise(constants.timeoutAzureFunctionFileError);
|
||||
await Promise.race([newFunctionFileObject.filePromise, timeoutForFunctionFile]);
|
||||
propertyBag.quickPickStep = quickPickStep;
|
||||
exitReason = 'finishCreate';
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.finishCreateAzureFunctionWithSqlBinding)
|
||||
.withAdditionalProperties(propertyBag)
|
||||
.withConnectionInfo(connectionInfo).send();
|
||||
} catch (e) {
|
||||
let errorType = utils.getErrorType(e);
|
||||
propertyBag.quickPickStep = quickPickStep;
|
||||
|
||||
if (errorType === 'TimeoutError') {
|
||||
// this error can be cause by many different scenarios including timeout or error occurred during createFunction
|
||||
exitReason = 'timeout';
|
||||
console.log('Timed out waiting for Azure Function project to be created. This may not necessarily be an error, for example if the user canceled out of the create flow.');
|
||||
} else {
|
||||
// else an error would occur during the createFunction
|
||||
exitReason = 'error';
|
||||
void vscode.window.showErrorMessage(utils.getErrorMessage(e));
|
||||
}
|
||||
TelemetryReporter.createErrorEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.exitCreateAzureFunctionQuickpick, undefined, errorType)
|
||||
.withAdditionalProperties(propertyBag).send();
|
||||
return;
|
||||
} finally {
|
||||
propertyBag.quickPickStep = quickPickStep;
|
||||
propertyBag.exitReason = exitReason;
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.exitCreateAzureFunctionQuickpick)
|
||||
.withConnectionInfo(connectionInfo)
|
||||
.withAdditionalProperties(propertyBag).send();
|
||||
} finally {
|
||||
propertyBag.telemetryStep = telemetryStep;
|
||||
propertyBag.exitReason = exitReason;
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.exitCreateAzureFunctionQuickpick)
|
||||
.withAdditionalProperties(propertyBag).send();
|
||||
if (newFunctionFileObject) {
|
||||
newFunctionFileObject.watcherDisposable.dispose();
|
||||
}
|
||||
} else {
|
||||
TelemetryReporter.sendErrorEvent(TelemetryViews.CreateAzureFunctionWithSqlBinding, TelemetryActions.finishCreateAzureFunctionWithSqlBinding);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user