/*--------------------------------------------------------------------------------------------- * 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 { 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 { 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`; } }