mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Add prompt to install required extensions for resource deployment (#14870)
This commit is contained in:
@@ -40,6 +40,10 @@ export const optionsTypeRadioOrDropdown = localize('optionsTypeRadioOrDropdown',
|
|||||||
export const azdataEulaNotAccepted = localize('azdataEulaNotAccepted', "Deployment cannot continue. Azure Data CLI license terms have not yet been accepted. Please accept the EULA to enable the features that requires Azure Data CLI.");
|
export const azdataEulaNotAccepted = localize('azdataEulaNotAccepted', "Deployment cannot continue. Azure Data CLI license terms have not yet been accepted. Please accept the EULA to enable the features that requires Azure Data CLI.");
|
||||||
export const azdataEulaDeclined = localize('azdataEulaDeclined', "Deployment cannot continue. Azure Data CLI license terms were declined.You can either Accept EULA to continue or Cancel this operation");
|
export const azdataEulaDeclined = localize('azdataEulaDeclined', "Deployment cannot continue. Azure Data CLI license terms were declined.You can either Accept EULA to continue or Cancel this operation");
|
||||||
export const acceptEulaAndSelect = localize('deploymentDialog.RecheckEulaButton', "Accept EULA & Select");
|
export const acceptEulaAndSelect = localize('deploymentDialog.RecheckEulaButton', "Accept EULA & Select");
|
||||||
|
export const extensionRequiredPrompt = (extensionName: string) => localize('resourceDeployment.extensionRequiredPrompt', "The '{0}' extension is required to deploy this resource, do you want to install it now?", extensionName);
|
||||||
|
export const install = localize('resourceDeployment.install', "Install");
|
||||||
|
export const installingExtension = (extensionName: string) => localize('resourceDeployment.installingExtension', "Installing extension '{0}'...", extensionName);
|
||||||
|
export const unknownExtension = (extensionId: string) => localize('resourceDeployment.unknownExtension', "Unknown extension '{0}'", extensionId);
|
||||||
|
|
||||||
export const resourceTypePickerDialogTitle = localize('resourceTypePickerDialog.title', "Select the deployment options");
|
export const resourceTypePickerDialogTitle = localize('resourceTypePickerDialog.title', "Select the deployment options");
|
||||||
export const resourceTypeSearchBoxDescription = localize('resourceTypePickerDialog.resourceSearchPlaceholder', "Filter resources...");
|
export const resourceTypeSearchBoxDescription = localize('resourceTypePickerDialog.resourceSearchPlaceholder', "Filter resources...");
|
||||||
|
|||||||
@@ -5,23 +5,74 @@
|
|||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { ResourceTypeService } from './resourceTypeService';
|
import { ResourceTypeService } from './resourceTypeService';
|
||||||
|
import * as loc from '../localizedConstants';
|
||||||
|
|
||||||
|
interface IGalleryExtension {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
date: string;
|
||||||
|
displayName: string;
|
||||||
|
publisherId: string;
|
||||||
|
publisher: string;
|
||||||
|
publisherDisplayName: string;
|
||||||
|
description: string;
|
||||||
|
preview: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export class UriHandlerService implements vscode.UriHandler {
|
export class UriHandlerService implements vscode.UriHandler {
|
||||||
constructor(private _resourceTypeService: ResourceTypeService) { }
|
constructor(private _resourceTypeService: ResourceTypeService) { }
|
||||||
|
|
||||||
handleUri(uri: vscode.Uri): vscode.ProviderResult<void> {
|
async handleUri(uri: vscode.Uri): Promise<void> {
|
||||||
// Path to start a deployment
|
// Path to start a deployment
|
||||||
// Supported URI parameters :
|
// Supported URI parameters :
|
||||||
// - type (optional) : The resource type to start the deployment for
|
// - type (optional) : The resource type to start the deployment for
|
||||||
|
// - extension (optional) : The ID of the extension that is required to start the deployment
|
||||||
// - params (optional) : A JSON blob of variable names/values to pass as initial values to the wizard. Note
|
// - params (optional) : A JSON blob of variable names/values to pass as initial values to the wizard. Note
|
||||||
// that the JSON blob must be URI-encoded in order to be properly handled
|
// that the JSON blob must be URI-encoded in order to be properly handled
|
||||||
// Example URIs :
|
// Example URIs :
|
||||||
// azuredatastudio://Microsoft.resource-deployment/deploy
|
// azuredatastudio://Microsoft.resource-deployment/deploy
|
||||||
// azuredatastudio://Microsoft.resource-deployment/deploy?type=arc-controller
|
// azuredatastudio://Microsoft.resource-deployment/deploy?type=arc-controller
|
||||||
|
// azuredatastudio://Microsoft.resource-deployment/deploy?type=arc-controller&extension=Microsoft.arc
|
||||||
// azuredatastudio://Microsoft.resource-deployment/deploy?type=arc-controller¶ms=%7B%22AZDATA_NB_VAR_ARC_SUBSCRIPTION%22%3A%22abdcef12-3456-7890-abcd-ef1234567890%22%2C%22AZDATA_NB_VAR_ARC_RESOURCE_GROUP%22%3A%22my-rg%22%2C%22AZDATA_NB_VAR_ARC_DATA_CONTROLLER_LOCATION%22%3A%22westus%22%2C%22AZDATA_NB_VAR_ARC_DATA_CONTROLLER_NAME%22%3A%22arc-dc%22%7D
|
// azuredatastudio://Microsoft.resource-deployment/deploy?type=arc-controller¶ms=%7B%22AZDATA_NB_VAR_ARC_SUBSCRIPTION%22%3A%22abdcef12-3456-7890-abcd-ef1234567890%22%2C%22AZDATA_NB_VAR_ARC_RESOURCE_GROUP%22%3A%22my-rg%22%2C%22AZDATA_NB_VAR_ARC_DATA_CONTROLLER_LOCATION%22%3A%22westus%22%2C%22AZDATA_NB_VAR_ARC_DATA_CONTROLLER_NAME%22%3A%22arc-dc%22%7D
|
||||||
if (uri.path === '/deploy') {
|
if (uri.path === '/deploy') {
|
||||||
const params = uri.query.split('&').map(kv => kv.split('='));
|
const params = uri.query.split('&').map(kv => kv.split('='));
|
||||||
const paramType = params.find(param => param[0] === 'type')?.[1];
|
const paramType = params.find(param => param[0] === 'type')?.[1];
|
||||||
|
const extensionId = params.find(param => param[0] === 'extension')?.[1];
|
||||||
|
if (extensionId) {
|
||||||
|
const installedExtension = vscode.extensions.getExtension(extensionId);
|
||||||
|
if (!installedExtension) {
|
||||||
|
// The required extension isn't installed, prompt user to install it
|
||||||
|
const extensionGalleryInfo = await vscode.commands.executeCommand<IGalleryExtension>('workbench.extensions.getExtensionFromGallery', extensionId);
|
||||||
|
if (extensionGalleryInfo) {
|
||||||
|
const response = await vscode.window.showInformationMessage(
|
||||||
|
loc.extensionRequiredPrompt(extensionGalleryInfo.displayName),
|
||||||
|
loc.install);
|
||||||
|
if (response === loc.install) {
|
||||||
|
await vscode.window.withProgress(
|
||||||
|
{
|
||||||
|
location: vscode.ProgressLocation.Notification,
|
||||||
|
title: loc.installingExtension(extensionGalleryInfo.displayName),
|
||||||
|
cancellable: false
|
||||||
|
}, async (_progress, _token) => {
|
||||||
|
await vscode.commands.executeCommand('workbench.extensions.installExtension', extensionId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// If user didn't install extension we wouldn't expect the deployment to work so just return
|
||||||
|
console.log(`User cancelled out of prompt to install required extension '${extensionId}' for Resource Deployment URI`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If we can't find the extension in the gallery then we won't be able to install it - so just inform the user
|
||||||
|
// that the ID is invalid and return since we wouldn't expect the deployment to work without the extension
|
||||||
|
vscode.window.showErrorMessage(loc.unknownExtension(extensionId));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Extension is already installed, ensure that it's activated before continuing on
|
||||||
|
await installedExtension.activate();
|
||||||
|
}
|
||||||
|
}
|
||||||
const wizardParams = JSON.parse(params.find(param => param[0] === 'params')?.[1] ?? '{}');
|
const wizardParams = JSON.parse(params.find(param => param[0] === 'params')?.[1] ?? '{}');
|
||||||
|
|
||||||
const resourceType = this._resourceTypeService.getResourceTypes().find(type => type.name === paramType);
|
const resourceType = this._resourceTypeService.getResourceTypes().find(type => type.name === paramType);
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation
|
|||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
|
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
|
||||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||||
import { ExtensionsLabel } from 'vs/platform/extensionManagement/common/extensionManagement';
|
import { ExtensionsLabel, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||||
import { OpenExtensionAuthoringDocsAction } from 'sql/workbench/contrib/extensions/browser/extensionsActions';
|
import { OpenExtensionAuthoringDocsAction } from 'sql/workbench/contrib/extensions/browser/extensionsActions';
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
|
import { deepClone } from 'vs/base/common/objects';
|
||||||
|
|
||||||
// Global Actions
|
// Global Actions
|
||||||
const actionRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
|
const actionRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
|
||||||
@@ -23,3 +25,27 @@ CommandsRegistry.registerCommand('azdata.extension.open', (accessor: ServicesAcc
|
|||||||
throw new Error('Extension id is not provided');
|
throw new Error('Extension id is not provided');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
CommandsRegistry.registerCommand({
|
||||||
|
id: 'workbench.extensions.getExtensionFromGallery',
|
||||||
|
description: {
|
||||||
|
description: localize('workbench.extensions.getExtensionFromGallery.description', "Gets extension information from the gallery"),
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
name: localize('workbench.extensions.getExtensionFromGallery.arg.name', "Extension id"),
|
||||||
|
schema: {
|
||||||
|
'type': ['string']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
handler: async (accessor, arg: string) => {
|
||||||
|
const extensionGalleryService = accessor.get(IExtensionGalleryService);
|
||||||
|
const extension = await extensionGalleryService.getCompatibleExtension({ id: arg });
|
||||||
|
if (extension) {
|
||||||
|
return deepClone(extension);
|
||||||
|
} else {
|
||||||
|
throw new Error(localize('notFound', "Extension '{0}' not found.", arg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user