Files
azuredatastudio/extensions/sql-database-projects/src/tools/autorestHelper.ts
Benjin Dubishar 5160814623 Dedupe shell command execution logic (#17516)
* Moved to shellExecutionHelper

* First crack

* fixed the deploy tests

* PR comments

* trigger GitHub actions

Co-authored-by: llali <llali@microsoft.com>
2021-11-04 17:16:58 -07:00

119 lines
4.8 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as constants from '../common/constants';
import * as utils from '../common/utils';
import * as semver from 'semver';
import { DBProjectConfigurationKey } from './netcoreTool';
import { ShellExecutionHelper } from './shellExecutionHelper';
const autorestPackageName = 'autorest-sql-testing'; // name of AutoRest.Sql package on npm
const nodejsDoNotAskAgainKey: string = 'nodejsDoNotAsk';
const autorestSqlVersionKey: string = 'autorestSqlVersion';
/**
* Helper class for dealing with Autorest generation and detection
*/
export class AutorestHelper extends ShellExecutionHelper {
constructor(_outputChannel: vscode.OutputChannel) {
super(_outputChannel);
}
/**
* Checks the workspace configuration to for an AutoRest.Sql override, otherwise latest will be used from NPM
*/
public get autorestSqlPackageVersion(): string {
let configVal: string | undefined = vscode.workspace.getConfiguration(DBProjectConfigurationKey)[autorestSqlVersionKey];
if (configVal && semver.valid(configVal.trim())) {
return configVal.trim();
} else {
return 'latest';
}
}
/**
* @returns the beginning of the command to execute autorest; 'autorest' if available, 'npx autorest' if module not installed, or undefined if neither
*/
public async detectInstallation(): Promise<string | undefined> {
const autorestCommand = 'autorest';
const npxCommand = 'npx';
if (await utils.detectCommandInstallation(autorestCommand)) {
return autorestCommand;
}
else if (await utils.detectCommandInstallation(npxCommand)) {
this._outputChannel.appendLine(constants.nodeButNotAutorestFound);
const response = await vscode.window.showInformationMessage(constants.nodeButNotAutorestFoundPrompt, constants.installGlobally, constants.runViaNpx);
if (response === constants.installGlobally) {
this._outputChannel.appendLine(constants.userSelectionInstallGlobally);
await this.runStreamedCommand('npm install autorest -g');
return autorestCommand;
} else if (response === constants.runViaNpx) {
this._outputChannel.appendLine(constants.userSelectionRunNpx);
return `${npxCommand} ${autorestCommand}`;
} else {
this._outputChannel.appendLine(constants.userSelectionCancelled);
}
}
else {
this._outputChannel.appendLine(constants.nodeNotFound);
}
return undefined;
}
/**
* Calls autorest to generate files from the spec, piping standard and error output to the host console
* @param specPath path to the OpenAPI spec file
* @param outputFolder folder in which to generate the .sql script files
* @returns console output from autorest execution
*/
public async generateAutorestFiles(specPath: string, outputFolder: string): Promise<string | undefined> {
const commandExecutable = await this.detectInstallation();
if (!commandExecutable) {
// unable to find autorest or npx
if (vscode.workspace.getConfiguration(DBProjectConfigurationKey)[nodejsDoNotAskAgainKey] !== true) {
return; // user doesn't want to be prompted about installing it
}
// prompt user to install Node.js
const result = await vscode.window.showErrorMessage(constants.nodeNotFound, constants.DoNotAskAgain, constants.Install);
if (result === constants.Install) {
//open install link
const nodejsInstallationUrl = 'https://nodejs.dev/download';
await vscode.env.openExternal(vscode.Uri.parse(nodejsInstallationUrl));
} else if (result === constants.DoNotAskAgain) {
const config = vscode.workspace.getConfiguration(DBProjectConfigurationKey);
await config.update(nodejsDoNotAskAgainKey, true, vscode.ConfigurationTarget.Global);
}
return;
}
const command = this.constructAutorestCommand(commandExecutable, specPath, outputFolder);
const output = await this.runStreamedCommand(command);
return output;
}
/**
*
* @param executable either "autorest" or "npx autorest", depending on whether autorest is already present in the global cache
* @param specPath path to the OpenAPI spec
* @param outputFolder folder in which to generate the files
* @returns composed command to be executed
*/
public constructAutorestCommand(executable: string, specPath: string, outputFolder: string): string {
// TODO: should --clear-output-folder be included? We should always be writing to a folder created just for this, but potentially risky
return `${executable} --use:${autorestPackageName}@${this.autorestSqlPackageVersion} --input-file="${specPath}" --output-folder="${outputFolder}" --clear-output-folder --verbose`;
}
}