mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
allow registering options source providers to resource-deployment (#12712)
* first draft * compile fixes * uncomment code * waitForAzdataToolDisovery added to azdata api * missed change in last commit * remove switchReturn * contributeOptionsSource renamed * remove switchReturn reference * create optionSourceService * azdataTool usage more reliable * package.json fixes and cleanup * cleanup * revert 4831a6e6b8b08684488b2c9e18092fa252e3057f * pr feedback * pr feedback * pr feedback * cleanup * cleanup * fix eulaAccepted check * fix whitespade in doc comments.
This commit is contained in:
@@ -18,7 +18,8 @@
|
|||||||
"onView:azureArc"
|
"onView:azureArc"
|
||||||
],
|
],
|
||||||
"extensionDependencies": [
|
"extensionDependencies": [
|
||||||
"Microsoft.azdata"
|
"Microsoft.azdata",
|
||||||
|
"Microsoft.resource-deployment"
|
||||||
],
|
],
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -186,7 +187,7 @@
|
|||||||
"editable": false,
|
"editable": false,
|
||||||
"options": {
|
"options": {
|
||||||
"source": {
|
"source": {
|
||||||
"type": "ArcControllerConfigProfilesOptionsSource"
|
"providerId": "arc.controller.config.profiles"
|
||||||
},
|
},
|
||||||
"defaultValue": "azure-arc-aks-default-storage",
|
"defaultValue": "azure-arc-aks-default-storage",
|
||||||
"optionsType": "radio"
|
"optionsType": "radio"
|
||||||
@@ -546,7 +547,7 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"options": {
|
"options": {
|
||||||
"source": {
|
"source": {
|
||||||
"type": "ArcControllersOptionsSource",
|
"providerId": "arc.controllers",
|
||||||
"variableNames": {
|
"variableNames": {
|
||||||
"endpoint": "AZDATA_NB_VAR_CONTROLLER_ENDPOINT",
|
"endpoint": "AZDATA_NB_VAR_CONTROLLER_ENDPOINT",
|
||||||
"username": "AZDATA_NB_VAR_CONTROLLER_USERNAME",
|
"username": "AZDATA_NB_VAR_CONTROLLER_USERNAME",
|
||||||
@@ -711,7 +712,7 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"options": {
|
"options": {
|
||||||
"source": {
|
"source": {
|
||||||
"type": "ArcControllersOptionsSource",
|
"providerId": "arc.controllers",
|
||||||
"variableNames": {
|
"variableNames": {
|
||||||
"endpoint": "AZDATA_NB_VAR_CONTROLLER_ENDPOINT",
|
"endpoint": "AZDATA_NB_VAR_CONTROLLER_ENDPOINT",
|
||||||
"username": "AZDATA_NB_VAR_CONTROLLER_USERNAME",
|
"username": "AZDATA_NB_VAR_CONTROLLER_USERNAME",
|
||||||
|
|||||||
41
extensions/arc/src/common/api.ts
Normal file
41
extensions/arc/src/common/api.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as arc from 'arc';
|
||||||
|
import { PasswordToControllerDialog } from '../ui/dialogs/connectControllerDialog';
|
||||||
|
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
||||||
|
import { ControllerTreeNode } from '../ui/tree/controllerTreeNode';
|
||||||
|
import { UserCancelledError } from './utils';
|
||||||
|
|
||||||
|
export function arcApi(treeDataProvider: AzureArcTreeDataProvider): arc.IExtension {
|
||||||
|
return {
|
||||||
|
getRegisteredDataControllers: () => getRegisteredDataControllers(treeDataProvider),
|
||||||
|
getControllerPassword: (controllerInfo: arc.ControllerInfo) => getControllerPassword(treeDataProvider, controllerInfo),
|
||||||
|
reacquireControllerPassword: (controllerInfo: arc.ControllerInfo) => reacquireControllerPassword(treeDataProvider, controllerInfo)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export async function reacquireControllerPassword(treeDataProvider: AzureArcTreeDataProvider, controllerInfo: arc.ControllerInfo): Promise<string> {
|
||||||
|
const dialog = new PasswordToControllerDialog(treeDataProvider);
|
||||||
|
dialog.showDialog(controllerInfo);
|
||||||
|
const model = await dialog.waitForClose();
|
||||||
|
if (!model) {
|
||||||
|
throw new UserCancelledError();
|
||||||
|
}
|
||||||
|
return model.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getControllerPassword(treeDataProvider: AzureArcTreeDataProvider, controllerInfo: arc.ControllerInfo): Promise<string> {
|
||||||
|
return await treeDataProvider.getPassword(controllerInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRegisteredDataControllers(treeDataProvider: AzureArcTreeDataProvider): Promise<arc.DataController[]> {
|
||||||
|
return (await treeDataProvider.getChildren())
|
||||||
|
.filter(node => node instanceof ControllerTreeNode)
|
||||||
|
.map(node => ({
|
||||||
|
label: (node as ControllerTreeNode).model.label,
|
||||||
|
info: (node as ControllerTreeNode).model.info
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
@@ -216,3 +216,17 @@ export function parseIpAndPort(address: string): { ip: string, port: string } {
|
|||||||
export function createCredentialId(controllerId: string, resourceType: string, instanceName: string): string {
|
export function createCredentialId(controllerId: string, resourceType: string, instanceName: string): string {
|
||||||
return `${controllerId}::${resourceType}::${instanceName}`;
|
return `${controllerId}::${resourceType}::${instanceName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an Error with given {@link message} unless {@link condition} is true.
|
||||||
|
* This also tells the typescript compiler that the condition is 'truthy' in the remainder of the scope
|
||||||
|
* where this function was called.
|
||||||
|
*
|
||||||
|
* @param condition
|
||||||
|
* @param message
|
||||||
|
*/
|
||||||
|
export function throwUnless(condition: boolean, message?: string): asserts condition {
|
||||||
|
if (!condition) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as arc from 'arc';
|
import * as arc from 'arc';
|
||||||
|
import * as rd from 'resource-deployment';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { UserCancelledError } from './common/utils';
|
import { arcApi } from './common/api';
|
||||||
import { IconPathHelper, refreshActionId } from './constants';
|
import { IconPathHelper, refreshActionId } from './constants';
|
||||||
import * as loc from './localizedConstants';
|
import * as loc from './localizedConstants';
|
||||||
import { ConnectToControllerDialog, PasswordToControllerDialog } from './ui/dialogs/connectControllerDialog';
|
import { ArcControllersOptionsSourceProvider } from './providers/arcControllersOptionsSourceProvider';
|
||||||
|
import { ConnectToControllerDialog } from './ui/dialogs/connectControllerDialog';
|
||||||
import { AzureArcTreeDataProvider } from './ui/tree/azureArcTreeDataProvider';
|
import { AzureArcTreeDataProvider } from './ui/tree/azureArcTreeDataProvider';
|
||||||
import { ControllerTreeNode } from './ui/tree/controllerTreeNode';
|
import { ControllerTreeNode } from './ui/tree/controllerTreeNode';
|
||||||
import { TreeNode } from './ui/tree/treeNode';
|
import { TreeNode } from './ui/tree/treeNode';
|
||||||
@@ -63,27 +65,11 @@ export async function activate(context: vscode.ExtensionContext): Promise<arc.IE
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
// register option sources
|
||||||
getRegisteredDataControllers: async () => (await treeDataProvider.getChildren())
|
const rdApi = <rd.IExtension>vscode.extensions.getExtension(rd.extension.name)?.exports;
|
||||||
.filter(node => node instanceof ControllerTreeNode)
|
rdApi.registerOptionsSourceProvider(new ArcControllersOptionsSourceProvider(treeDataProvider));
|
||||||
.map(node => ({
|
|
||||||
label: (node as ControllerTreeNode).model.label,
|
return arcApi(treeDataProvider);
|
||||||
info: (node as ControllerTreeNode).model.info
|
|
||||||
})),
|
|
||||||
getControllerPassword: async (controllerInfo: arc.ControllerInfo) => {
|
|
||||||
return await treeDataProvider.getPassword(controllerInfo);
|
|
||||||
},
|
|
||||||
reacquireControllerPassword: async (controllerInfo: arc.ControllerInfo) => {
|
|
||||||
let model;
|
|
||||||
const dialog = new PasswordToControllerDialog(treeDataProvider);
|
|
||||||
dialog.showDialog(controllerInfo);
|
|
||||||
model = await dialog.waitForClose();
|
|
||||||
if (!model) {
|
|
||||||
throw new UserCancelledError();
|
|
||||||
}
|
|
||||||
return model.password;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deactivate(): void {
|
export function deactivate(): void {
|
||||||
|
|||||||
@@ -168,3 +168,8 @@ export function passwordAcquisitionFailed(error: any): string { return localize(
|
|||||||
export const invalidPassword = localize('arc.invalidPassword', "The password did not work, try again.");
|
export const invalidPassword = localize('arc.invalidPassword', "The password did not work, try again.");
|
||||||
export function errorVerifyingPassword(error: any): string { return localize('arc.errorVerifyingPassword', "Error encountered while verifying password. {0}", getErrorMessage(error)); }
|
export function errorVerifyingPassword(error: any): string { return localize('arc.errorVerifyingPassword', "Error encountered while verifying password. {0}", getErrorMessage(error)); }
|
||||||
export const onlyOneControllerSupported = localize('arc.onlyOneControllerSupported', "Only one controller connection is currently supported at this time. Do you wish to remove the existing connection and add a new one?");
|
export const onlyOneControllerSupported = localize('arc.onlyOneControllerSupported', "Only one controller connection is currently supported at this time. Do you wish to remove the existing connection and add a new one?");
|
||||||
|
export const noControllersConnected = localize('noControllersConnected', "No Azure Arc controllers are currently connected. Please run the command: 'Connect to Existing Azure Arc Controller' and then try again");
|
||||||
|
export const variableValueFetchForUnsupportedVariable = (variableName: string) => localize('getVariableValue.unknownVariableName', "Attempt to get variable value for unknown variable:{0}", variableName);
|
||||||
|
export const isPasswordFetchForUnsupportedVariable = (variableName: string) => localize('getIsPassword.unknownVariableName', "Attempt to get isPassword for unknown variable:{0}", variableName);
|
||||||
|
export const noControllerInfoFound = (name: string) => localize('noControllerInfoFound', "Controller Info could not be found with name: {0}", name);
|
||||||
|
export const noPasswordFound = (controllerName: string) => localize('noPasswordFound', "Password could not be retrieved for controller: {0} and user did not provide a password. Please retry later.", controllerName);
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as arc from 'arc';
|
||||||
|
import * as azdata from 'azdata';
|
||||||
|
import * as rd from 'resource-deployment';
|
||||||
|
import { getControllerPassword, getRegisteredDataControllers, reacquireControllerPassword } from '../common/api';
|
||||||
|
import { CacheManager } from '../common/cacheManager';
|
||||||
|
import { throwUnless } from '../common/utils';
|
||||||
|
import * as loc from '../localizedConstants';
|
||||||
|
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that provides options sources for an Arc Data Controller
|
||||||
|
*/
|
||||||
|
export class ArcControllersOptionsSourceProvider implements rd.IOptionsSourceProvider {
|
||||||
|
private _cacheManager = new CacheManager<string, string>();
|
||||||
|
readonly optionsSourceId = 'arc.controllers';
|
||||||
|
constructor(private _treeProvider: AzureArcTreeDataProvider) { }
|
||||||
|
|
||||||
|
async getOptions(): Promise<string[] | azdata.CategoryValue[]> {
|
||||||
|
const controllers = await getRegisteredDataControllers(this._treeProvider);
|
||||||
|
throwUnless(controllers !== undefined && controllers.length !== 0, loc.noControllersConnected);
|
||||||
|
return controllers.map(ci => {
|
||||||
|
return ci.label;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async retrieveVariable(key: string): Promise<string> {
|
||||||
|
const [variableName, controllerLabel] = JSON.parse(key);
|
||||||
|
const controller = (await getRegisteredDataControllers(this._treeProvider)).find(ci => ci.label === controllerLabel);
|
||||||
|
throwUnless(controller !== undefined, loc.noControllerInfoFound(controllerLabel));
|
||||||
|
switch (variableName) {
|
||||||
|
case 'endpoint': return controller.info.url;
|
||||||
|
case 'username': return controller.info.username;
|
||||||
|
case 'password': return this.getPassword(controller);
|
||||||
|
default: throw new Error(loc.variableValueFetchForUnsupportedVariable(variableName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getVariableValue(variableName: string, controllerLabel: string): Promise<string> {
|
||||||
|
return this._cacheManager.getCacheEntry(JSON.stringify([variableName, controllerLabel]), this.retrieveVariable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getPassword(controller: arc.DataController): Promise<string> {
|
||||||
|
let password = await getControllerPassword(this._treeProvider, controller.info);
|
||||||
|
if (!password) {
|
||||||
|
password = await reacquireControllerPassword(this._treeProvider, controller.info);
|
||||||
|
}
|
||||||
|
throwUnless(password !== undefined, loc.noPasswordFound(controller.label));
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
getIsPassword(variableName: string): boolean {
|
||||||
|
switch (variableName) {
|
||||||
|
case 'endpoint': return false;
|
||||||
|
case 'username': return false;
|
||||||
|
case 'password': return true;
|
||||||
|
default: throw new Error(loc.isPasswordFetchForUnsupportedVariable(variableName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
extensions/arc/src/typings/refs.d.ts
vendored
1
extensions/arc/src/typings/refs.d.ts
vendored
@@ -8,3 +8,4 @@
|
|||||||
/// <reference path='../../../azurecore/src/azurecore.d.ts'/>
|
/// <reference path='../../../azurecore/src/azurecore.d.ts'/>
|
||||||
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||||
/// <reference path='../../../azdata/src/typings/azdata-ext.d.ts'/>
|
/// <reference path='../../../azdata/src/typings/azdata-ext.d.ts'/>
|
||||||
|
/// <reference path='../../../resource-deployment/src/typings/resource-deployment.d.ts'/>
|
||||||
|
|||||||
@@ -18,6 +18,9 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Microsoft/azuredatastudio.git"
|
"url": "https://github.com/Microsoft/azuredatastudio.git"
|
||||||
},
|
},
|
||||||
|
"extensionDependencies": [
|
||||||
|
"microsoft.resource-deployment"
|
||||||
|
],
|
||||||
"main": "./out/extension",
|
"main": "./out/extension",
|
||||||
"contributes": {
|
"contributes": {
|
||||||
"configuration": [
|
"configuration": [
|
||||||
|
|||||||
153
extensions/azdata/src/api.ts
Normal file
153
extensions/azdata/src/api.ts
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as azdataExt from 'azdata-ext';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import { IAzdataTool, isEulaAccepted, promptForEula } from './azdata';
|
||||||
|
import Logger from './common/logger';
|
||||||
|
import { NoAzdataError } from './common/utils';
|
||||||
|
import * as constants from './constants';
|
||||||
|
import * as loc from './localizedConstants';
|
||||||
|
import { AzdataToolService } from './services/azdataToolService';
|
||||||
|
|
||||||
|
function throwIfNoAzdataOrEulaNotAccepted(azdata: IAzdataTool | undefined, eulaAccepted: boolean): asserts azdata {
|
||||||
|
throwIfNoAzdata(azdata);
|
||||||
|
if (!eulaAccepted) {
|
||||||
|
Logger.log(loc.eulaNotAccepted);
|
||||||
|
throw new Error(loc.eulaNotAccepted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function throwIfNoAzdata(localAzdata: IAzdataTool | undefined): asserts localAzdata {
|
||||||
|
if (!localAzdata) {
|
||||||
|
Logger.log(loc.noAzdata);
|
||||||
|
throw new NoAzdataError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getExtensionApi(memento: vscode.Memento, azdataToolService: AzdataToolService, localAzdataDiscovered: Promise<IAzdataTool | undefined>): azdataExt.IExtension {
|
||||||
|
return {
|
||||||
|
isEulaAccepted: async () => {
|
||||||
|
throwIfNoAzdata(await localAzdataDiscovered); // ensure that we have discovered Azdata
|
||||||
|
return !!memento.get<boolean>(constants.eulaAccepted);
|
||||||
|
},
|
||||||
|
promptForEula: async (requireUserAction: boolean = true): Promise<boolean> => {
|
||||||
|
await localAzdataDiscovered;
|
||||||
|
return promptForEula(memento, true /* userRequested */, requireUserAction);
|
||||||
|
},
|
||||||
|
azdata: getAzdataApi(localAzdataDiscovered, azdataToolService, memento)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAzdataApi(localAzdataDiscovered: Promise<IAzdataTool | undefined>, azdataToolService: AzdataToolService, memento: vscode.Memento): azdataExt.IAzdataApi {
|
||||||
|
return {
|
||||||
|
arc: {
|
||||||
|
dc: {
|
||||||
|
create: async (namespace: string, name: string, connectivityMode: string, resourceGroup: string, location: string, subscription: string, profileName?: string, storageClass?: string) => {
|
||||||
|
await localAzdataDiscovered;
|
||||||
|
throwIfNoAzdataOrEulaNotAccepted(azdataToolService.localAzdata, isEulaAccepted(memento));
|
||||||
|
return azdataToolService.localAzdata.arc.dc.create(namespace, name, connectivityMode, resourceGroup, location, subscription, profileName, storageClass);
|
||||||
|
},
|
||||||
|
endpoint: {
|
||||||
|
list: async () => {
|
||||||
|
await localAzdataDiscovered;
|
||||||
|
throwIfNoAzdataOrEulaNotAccepted(azdataToolService.localAzdata, isEulaAccepted(memento));
|
||||||
|
return azdataToolService.localAzdata.arc.dc.endpoint.list();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
list: async () => {
|
||||||
|
await localAzdataDiscovered;
|
||||||
|
throwIfNoAzdataOrEulaNotAccepted(azdataToolService.localAzdata, isEulaAccepted(memento));
|
||||||
|
return azdataToolService.localAzdata.arc.dc.config.list();
|
||||||
|
},
|
||||||
|
show: async () => {
|
||||||
|
await localAzdataDiscovered;
|
||||||
|
throwIfNoAzdataOrEulaNotAccepted(azdataToolService.localAzdata, isEulaAccepted(memento));
|
||||||
|
return azdataToolService.localAzdata.arc.dc.config.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
postgres: {
|
||||||
|
server: {
|
||||||
|
delete: async (name: string) => {
|
||||||
|
await localAzdataDiscovered;
|
||||||
|
throwIfNoAzdataOrEulaNotAccepted(azdataToolService.localAzdata, isEulaAccepted(memento));
|
||||||
|
return azdataToolService.localAzdata.arc.postgres.server.delete(name);
|
||||||
|
},
|
||||||
|
list: async () => {
|
||||||
|
await localAzdataDiscovered;
|
||||||
|
throwIfNoAzdataOrEulaNotAccepted(azdataToolService.localAzdata, isEulaAccepted(memento));
|
||||||
|
return azdataToolService.localAzdata.arc.postgres.server.list();
|
||||||
|
},
|
||||||
|
show: async (name: string) => {
|
||||||
|
await localAzdataDiscovered;
|
||||||
|
throwIfNoAzdataOrEulaNotAccepted(azdataToolService.localAzdata, isEulaAccepted(memento));
|
||||||
|
return azdataToolService.localAzdata.arc.postgres.server.show(name);
|
||||||
|
},
|
||||||
|
edit: async (
|
||||||
|
name: string,
|
||||||
|
args: {
|
||||||
|
adminPassword?: boolean;
|
||||||
|
coresLimit?: string;
|
||||||
|
coresRequest?: string;
|
||||||
|
engineSettings?: string;
|
||||||
|
extensions?: string;
|
||||||
|
memoryLimit?: string;
|
||||||
|
memoryRequest?: string;
|
||||||
|
noWait?: boolean;
|
||||||
|
port?: number;
|
||||||
|
replaceEngineSettings?: boolean;
|
||||||
|
workers?: number;
|
||||||
|
},
|
||||||
|
additionalEnvVars?: { [key: string]: string; }) => {
|
||||||
|
await localAzdataDiscovered;
|
||||||
|
throwIfNoAzdataOrEulaNotAccepted(azdataToolService.localAzdata, isEulaAccepted(memento));
|
||||||
|
return azdataToolService.localAzdata.arc.postgres.server.edit(name, args, additionalEnvVars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sql: {
|
||||||
|
mi: {
|
||||||
|
delete: async (name: string) => {
|
||||||
|
await localAzdataDiscovered;
|
||||||
|
throwIfNoAzdataOrEulaNotAccepted(azdataToolService.localAzdata, isEulaAccepted(memento));
|
||||||
|
return azdataToolService.localAzdata.arc.sql.mi.delete(name);
|
||||||
|
},
|
||||||
|
list: async () => {
|
||||||
|
await localAzdataDiscovered;
|
||||||
|
throwIfNoAzdataOrEulaNotAccepted(azdataToolService.localAzdata, isEulaAccepted(memento));
|
||||||
|
return azdataToolService.localAzdata.arc.sql.mi.list();
|
||||||
|
},
|
||||||
|
show: async (name: string) => {
|
||||||
|
await localAzdataDiscovered;
|
||||||
|
throwIfNoAzdataOrEulaNotAccepted(azdataToolService.localAzdata, isEulaAccepted(memento));
|
||||||
|
return azdataToolService.localAzdata.arc.sql.mi.show(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getPath: async () => {
|
||||||
|
await localAzdataDiscovered;
|
||||||
|
throwIfNoAzdata(azdataToolService.localAzdata);
|
||||||
|
return azdataToolService.localAzdata.getPath();
|
||||||
|
},
|
||||||
|
login: async (endpoint: string, username: string, password: string) => {
|
||||||
|
throwIfNoAzdataOrEulaNotAccepted(azdataToolService.localAzdata, isEulaAccepted(memento));
|
||||||
|
return azdataToolService.localAzdata.login(endpoint, username, password);
|
||||||
|
},
|
||||||
|
getSemVersion: async () => {
|
||||||
|
await localAzdataDiscovered;
|
||||||
|
throwIfNoAzdata(azdataToolService.localAzdata);
|
||||||
|
return azdataToolService.localAzdata.getSemVersion();
|
||||||
|
},
|
||||||
|
version: async () => {
|
||||||
|
await localAzdataDiscovered;
|
||||||
|
throwIfNoAzdata(azdataToolService.localAzdata);
|
||||||
|
return azdataToolService.localAzdata.version();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ export interface IAzdataTool extends azdataExt.IAzdataApi {
|
|||||||
/**
|
/**
|
||||||
* An object to interact with the azdata tool installed on the box.
|
* An object to interact with the azdata tool installed on the box.
|
||||||
*/
|
*/
|
||||||
export class AzdataTool implements IAzdataTool {
|
export class AzdataTool implements azdataExt.IAzdataApi {
|
||||||
|
|
||||||
private _semVersion: SemVer;
|
private _semVersion: SemVer;
|
||||||
constructor(private _path: string, version: string) {
|
constructor(private _path: string, version: string) {
|
||||||
@@ -49,14 +49,14 @@ export class AzdataTool implements IAzdataTool {
|
|||||||
* before fetching this value to ensure that correct value is returned. This is almost always correct unless
|
* before fetching this value to ensure that correct value is returned. This is almost always correct unless
|
||||||
* Azdata has gotten reinstalled in the background after this IAzdataApi object was constructed.
|
* Azdata has gotten reinstalled in the background after this IAzdataApi object was constructed.
|
||||||
*/
|
*/
|
||||||
public getSemVersion() {
|
public async getSemVersion(): Promise<SemVer> {
|
||||||
return this._semVersion;
|
return this._semVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gets the path where azdata tool is installed
|
* gets the path where azdata tool is installed
|
||||||
*/
|
*/
|
||||||
public getPath() {
|
public async getPath(): Promise<string> {
|
||||||
return this._path;
|
return this._path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,7 +225,7 @@ export async function findAzdata(): Promise<IAzdataTool> {
|
|||||||
try {
|
try {
|
||||||
const azdata = await findSpecificAzdata();
|
const azdata = await findSpecificAzdata();
|
||||||
await vscode.commands.executeCommand('setContext', azdataFound, true); // save a context key that azdata was found so that command for installing azdata is no longer available in commandPalette and that for updating it is.
|
await vscode.commands.executeCommand('setContext', azdataFound, true); // save a context key that azdata was found so that command for installing azdata is no longer available in commandPalette and that for updating it is.
|
||||||
Logger.log(loc.foundExistingAzdata(azdata.getPath(), azdata.getSemVersion().raw));
|
Logger.log(loc.foundExistingAzdata(await azdata.getPath(), (await azdata.getSemVersion()).raw));
|
||||||
return azdata;
|
return azdata;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Logger.log(loc.couldNotFindAzdata(err));
|
Logger.log(loc.couldNotFindAzdata(err));
|
||||||
@@ -312,11 +312,11 @@ export async function checkAndInstallAzdata(userRequested: boolean = false): Pro
|
|||||||
export async function checkAndUpdateAzdata(currentAzdata?: IAzdataTool, userRequested: boolean = false): Promise<boolean> {
|
export async function checkAndUpdateAzdata(currentAzdata?: IAzdataTool, userRequested: boolean = false): Promise<boolean> {
|
||||||
if (currentAzdata !== undefined) {
|
if (currentAzdata !== undefined) {
|
||||||
const newSemVersion = await discoverLatestAvailableAzdataVersion();
|
const newSemVersion = await discoverLatestAvailableAzdataVersion();
|
||||||
if (newSemVersion.compare(currentAzdata.getSemVersion()) === 1) {
|
if (newSemVersion.compare(await currentAzdata.getSemVersion()) === 1) {
|
||||||
Logger.log(loc.foundAzdataVersionToUpdateTo(newSemVersion.raw, currentAzdata.getSemVersion().raw));
|
Logger.log(loc.foundAzdataVersionToUpdateTo(newSemVersion.raw, (await currentAzdata.getSemVersion()).raw));
|
||||||
return await promptToUpdateAzdata(newSemVersion.raw, userRequested);
|
return await promptToUpdateAzdata(newSemVersion.raw, userRequested);
|
||||||
} else {
|
} else {
|
||||||
Logger.log(loc.currentlyInstalledVersionIsLatest(currentAzdata.getSemVersion().raw));
|
Logger.log(loc.currentlyInstalledVersionIsLatest((await currentAzdata.getSemVersion()).raw));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Logger.log(loc.updateCheckSkipped);
|
Logger.log(loc.updateCheckSkipped);
|
||||||
@@ -413,6 +413,17 @@ async function promptToUpdateAzdata(newVersion: string, userRequested: boolean =
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if Eula has been accepted.
|
||||||
|
*
|
||||||
|
* @param memento The memento that stores the eulaAccepted state
|
||||||
|
*/
|
||||||
|
export function isEulaAccepted(memento: vscode.Memento): boolean {
|
||||||
|
return !!memento.get<boolean>(eulaAccepted);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompts user to accept EULA. Stores and returns the user response to EULA prompt.
|
* Prompts user to accept EULA. Stores and returns the user response to EULA prompt.
|
||||||
* @param memento - memento where the user response is stored.
|
* @param memento - memento where the user response is stored.
|
||||||
|
|||||||
@@ -4,40 +4,42 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as azdataExt from 'azdata-ext';
|
import * as azdataExt from 'azdata-ext';
|
||||||
|
import * as rd from 'resource-deployment';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { checkAndInstallAzdata, checkAndUpdateAzdata, findAzdata, IAzdataTool, promptForEula } from './azdata';
|
import { getExtensionApi } from './api';
|
||||||
|
import { checkAndInstallAzdata, checkAndUpdateAzdata, findAzdata, isEulaAccepted, promptForEula } from './azdata';
|
||||||
import Logger from './common/logger';
|
import Logger from './common/logger';
|
||||||
import { NoAzdataError } from './common/utils';
|
|
||||||
import * as constants from './constants';
|
import * as constants from './constants';
|
||||||
import * as loc from './localizedConstants';
|
import * as loc from './localizedConstants';
|
||||||
|
import { ArcControllerConfigProfilesOptionsSource } from './providers/arcControllerConfigProfilesOptionsSource';
|
||||||
|
import { AzdataToolService } from './services/azdataToolService';
|
||||||
|
|
||||||
let localAzdata: IAzdataTool | undefined = undefined;
|
|
||||||
let eulaAccepted: boolean = false;
|
|
||||||
export async function activate(context: vscode.ExtensionContext): Promise<azdataExt.IExtension> {
|
export async function activate(context: vscode.ExtensionContext): Promise<azdataExt.IExtension> {
|
||||||
|
const azdataToolService = new AzdataToolService();
|
||||||
|
let eulaAccepted: boolean = false;
|
||||||
vscode.commands.registerCommand('azdata.acceptEula', async () => {
|
vscode.commands.registerCommand('azdata.acceptEula', async () => {
|
||||||
eulaAccepted = await promptForEula(context.globalState, true /* userRequested */);
|
await promptForEula(context.globalState, true /* userRequested */);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
vscode.commands.registerCommand('azdata.install', async () => {
|
vscode.commands.registerCommand('azdata.install', async () => {
|
||||||
localAzdata = await checkAndInstallAzdata(true /* userRequested */);
|
azdataToolService.localAzdata = await checkAndInstallAzdata(true /* userRequested */);
|
||||||
});
|
});
|
||||||
|
|
||||||
vscode.commands.registerCommand('azdata.update', async () => {
|
vscode.commands.registerCommand('azdata.update', async () => {
|
||||||
if (await checkAndUpdateAzdata(localAzdata, true /* userRequested */)) { // if an update was performed
|
if (await checkAndUpdateAzdata(azdataToolService.localAzdata, true /* userRequested */)) { // if an update was performed
|
||||||
localAzdata = await findAzdata(); // find and save the currently installed azdata
|
azdataToolService.localAzdata = await findAzdata(); // find and save the currently installed azdata
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
eulaAccepted = !!context.globalState.get<boolean>(constants.eulaAccepted); // fetch eula acceptance state from memento
|
eulaAccepted = isEulaAccepted(context.globalState); // fetch eula acceptance state from memento
|
||||||
await vscode.commands.executeCommand('setContext', constants.eulaAccepted, eulaAccepted); // set a context key for current value of eulaAccepted state retrieved from memento so that command for accepting eula is available/unavailable in commandPalette appropriately.
|
await vscode.commands.executeCommand('setContext', constants.eulaAccepted, eulaAccepted); // set a context key for current value of eulaAccepted state retrieved from memento so that command for accepting eula is available/unavailable in commandPalette appropriately.
|
||||||
Logger.log(loc.eulaAcceptedStateOnStartup(eulaAccepted));
|
Logger.log(loc.eulaAcceptedStateOnStartup(eulaAccepted));
|
||||||
|
|
||||||
// Don't block on this since we want the extension to finish activating without needing user input
|
// Don't block on this since we want the extension to finish activating without needing user input
|
||||||
checkAndInstallAzdata() // install if not installed and user wants it.
|
const localAzdataDiscovered = checkAndInstallAzdata() // install if not installed and user wants it.
|
||||||
.then(async azdataTool => {
|
.then(async azdataTool => {
|
||||||
localAzdata = azdataTool;
|
if (azdataTool !== undefined) {
|
||||||
if (localAzdata !== undefined) {
|
azdataToolService.localAzdata = azdataTool;
|
||||||
if (!eulaAccepted) {
|
if (!eulaAccepted) {
|
||||||
// Don't block on this since we want extension to finish activating without requiring user actions.
|
// Don't block on this since we want extension to finish activating without requiring user actions.
|
||||||
// If EULA has not been accepted then we will check again while executing azdata commands.
|
// If EULA has not been accepted then we will check again while executing azdata commands.
|
||||||
@@ -49,127 +51,23 @@ export async function activate(context: vscode.ExtensionContext): Promise<azdata
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
//update if available and user wants it.
|
//update if available and user wants it.
|
||||||
if (await checkAndUpdateAzdata(localAzdata)) { // if an update was performed
|
if (await checkAndUpdateAzdata(azdataToolService.localAzdata)) { // if an update was performed
|
||||||
localAzdata = await findAzdata(); // find and save the currently installed azdata
|
azdataToolService.localAzdata = await findAzdata(); // find and save the currently installed azdata
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
vscode.window.showWarningMessage(loc.updateError(err));
|
vscode.window.showWarningMessage(loc.updateError(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return azdataTool;
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
const azdataApi = getExtensionApi(context.globalState, azdataToolService, localAzdataDiscovered);
|
||||||
isEulaAccepted: () => !!context.globalState.get<boolean>(constants.eulaAccepted),
|
|
||||||
promptForEula: (onError: boolean = true): Promise<boolean> => promptForEula(context.globalState, true /* userRequested */, onError),
|
|
||||||
azdata: {
|
|
||||||
arc: {
|
|
||||||
dc: {
|
|
||||||
create: async (namespace: string, name: string, connectivityMode: string, resourceGroup: string, location: string, subscription: string, profileName?: string, storageClass?: string) => {
|
|
||||||
throwIfNoAzdataOrEulaNotAccepted();
|
|
||||||
return localAzdata!.arc.dc.create(namespace, name, connectivityMode, resourceGroup, location, subscription, profileName, storageClass);
|
|
||||||
},
|
|
||||||
endpoint: {
|
|
||||||
list: async () => {
|
|
||||||
throwIfNoAzdataOrEulaNotAccepted();
|
|
||||||
return localAzdata!.arc.dc.endpoint.list();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
list: async () => {
|
|
||||||
throwIfNoAzdataOrEulaNotAccepted();
|
|
||||||
return localAzdata!.arc.dc.config.list();
|
|
||||||
},
|
|
||||||
show: async () => {
|
|
||||||
throwIfNoAzdataOrEulaNotAccepted();
|
|
||||||
return localAzdata!.arc.dc.config.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
postgres: {
|
|
||||||
server: {
|
|
||||||
delete: async (name: string) => {
|
|
||||||
throwIfNoAzdataOrEulaNotAccepted();
|
|
||||||
return localAzdata!.arc.postgres.server.delete(name);
|
|
||||||
},
|
|
||||||
list: async () => {
|
|
||||||
throwIfNoAzdataOrEulaNotAccepted();
|
|
||||||
return localAzdata!.arc.postgres.server.list();
|
|
||||||
},
|
|
||||||
show: async (name: string) => {
|
|
||||||
throwIfNoAzdataOrEulaNotAccepted();
|
|
||||||
return localAzdata!.arc.postgres.server.show(name);
|
|
||||||
},
|
|
||||||
edit: async (
|
|
||||||
name: string,
|
|
||||||
args: {
|
|
||||||
adminPassword?: boolean,
|
|
||||||
coresLimit?: string,
|
|
||||||
coresRequest?: string,
|
|
||||||
engineSettings?: string,
|
|
||||||
extensions?: string,
|
|
||||||
memoryLimit?: string,
|
|
||||||
memoryRequest?: string,
|
|
||||||
noWait?: boolean,
|
|
||||||
port?: number,
|
|
||||||
replaceEngineSettings?: boolean,
|
|
||||||
workers?: number
|
|
||||||
},
|
|
||||||
additionalEnvVars?: { [key: string]: string }) => {
|
|
||||||
throwIfNoAzdataOrEulaNotAccepted();
|
|
||||||
return localAzdata!.arc.postgres.server.edit(name, args, additionalEnvVars);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sql: {
|
|
||||||
mi: {
|
|
||||||
delete: async (name: string) => {
|
|
||||||
throwIfNoAzdataOrEulaNotAccepted();
|
|
||||||
return localAzdata!.arc.sql.mi.delete(name);
|
|
||||||
},
|
|
||||||
list: async () => {
|
|
||||||
throwIfNoAzdataOrEulaNotAccepted();
|
|
||||||
return localAzdata!.arc.sql.mi.list();
|
|
||||||
},
|
|
||||||
show: async (name: string) => {
|
|
||||||
throwIfNoAzdataOrEulaNotAccepted();
|
|
||||||
return localAzdata!.arc.sql.mi.show(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getPath: () => {
|
|
||||||
throwIfNoAzdata();
|
|
||||||
return localAzdata!.getPath();
|
|
||||||
},
|
|
||||||
login: async (endpoint: string, username: string, password: string) => {
|
|
||||||
throwIfNoAzdataOrEulaNotAccepted();
|
|
||||||
return localAzdata!.login(endpoint, username, password);
|
|
||||||
},
|
|
||||||
getSemVersion: () => {
|
|
||||||
throwIfNoAzdata();
|
|
||||||
return localAzdata!.getSemVersion();
|
|
||||||
},
|
|
||||||
version: async () => {
|
|
||||||
throwIfNoAzdata();
|
|
||||||
return localAzdata!.version();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function throwIfNoAzdataOrEulaNotAccepted(): void {
|
// register option source(s)
|
||||||
throwIfNoAzdata();
|
const rdApi = <rd.IExtension>vscode.extensions.getExtension(rd.extension.name)?.exports;
|
||||||
if (!eulaAccepted) {
|
rdApi.registerOptionsSourceProvider(new ArcControllerConfigProfilesOptionsSource(azdataApi));
|
||||||
Logger.log(loc.eulaNotAccepted);
|
|
||||||
throw new Error(loc.eulaNotAccepted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function throwIfNoAzdata() {
|
return azdataApi;
|
||||||
if (!localAzdata) {
|
|
||||||
Logger.log(loc.noAzdata);
|
|
||||||
throw new NoAzdataError();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deactivate(): void { }
|
export function deactivate(): void { }
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as rd from 'resource-deployment';
|
||||||
|
import * as azdataExt from 'azdata-ext';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that provides options sources for an Arc Data Controller
|
||||||
|
*/
|
||||||
|
export class ArcControllerConfigProfilesOptionsSource implements rd.IOptionsSourceProvider {
|
||||||
|
readonly optionsSourceId = 'arc.controller.config.profiles';
|
||||||
|
constructor(private _azdataExtApi: azdataExt.IExtension) { }
|
||||||
|
async getOptions(): Promise<string[]> {
|
||||||
|
if (!this._azdataExtApi.isEulaAccepted()) { // if eula has not yet be accepted then give user a chance to accept it
|
||||||
|
await this._azdataExtApi.promptForEula();
|
||||||
|
}
|
||||||
|
return (await this._azdataExtApi.azdata.arc.dc.config.list()).result;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
extensions/azdata/src/services/azdataToolService.ts
Normal file
29
extensions/azdata/src/services/azdataToolService.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { IAzdataTool } from '../azdata';
|
||||||
|
|
||||||
|
export class AzdataToolService {
|
||||||
|
private _localAzdata: IAzdataTool | undefined;
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the localAzdata that was last saved
|
||||||
|
*/
|
||||||
|
get localAzdata(): IAzdataTool | undefined {
|
||||||
|
return this._localAzdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the localAzdata that was last saved
|
||||||
|
*
|
||||||
|
* @param memento The memento that stores the localAzdata object
|
||||||
|
*/
|
||||||
|
set localAzdata(azdata: IAzdataTool | undefined) {
|
||||||
|
this._localAzdata = azdata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -267,14 +267,14 @@ declare module 'azdata-ext' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getPath(): string,
|
getPath(): Promise<string>,
|
||||||
login(endpoint: string, username: string, password: string): Promise<AzdataOutput<any>>,
|
login(endpoint: string, username: string, password: string): Promise<AzdataOutput<any>>,
|
||||||
/**
|
/**
|
||||||
* The semVersion corresponding to this installation of azdata. version() method should have been run
|
* The semVersion corresponding to this installation of azdata. version() method should have been run
|
||||||
* before fetching this value to ensure that correct value is returned. This is almost always correct unless
|
* before fetching this value to ensure that correct value is returned. This is almost always correct unless
|
||||||
* Azdata has gotten reinstalled in the background after this IAzdataApi object was constructed.
|
* Azdata has gotten reinstalled in the background after this IAzdataApi object was constructed.
|
||||||
*/
|
*/
|
||||||
getSemVersion(): SemVer,
|
getSemVersion(): Promise<SemVer>,
|
||||||
version(): Promise<AzdataOutput<string>>
|
version(): Promise<AzdataOutput<string>>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +284,7 @@ declare module 'azdata-ext' {
|
|||||||
/**
|
/**
|
||||||
* returns true if AZDATA CLI EULA has been previously accepted by the user.
|
* returns true if AZDATA CLI EULA has been previously accepted by the user.
|
||||||
*/
|
*/
|
||||||
isEulaAccepted(): boolean;
|
isEulaAccepted(): Promise<boolean>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompts user to accept EULA. Stores and returns the user response to EULA prompt.
|
* Prompts user to accept EULA. Stores and returns the user response to EULA prompt.
|
||||||
@@ -293,6 +293,7 @@ declare module 'azdata-ext' {
|
|||||||
* pre-requisite, the calling code has to ensure that the EULA has not yet been previously accepted by the user. The code can use @see isEulaAccepted() call to ascertain this.
|
* pre-requisite, the calling code has to ensure that the EULA has not yet been previously accepted by the user. The code can use @see isEulaAccepted() call to ascertain this.
|
||||||
* returns true if the user accepted the EULA.
|
* returns true if the user accepted the EULA.
|
||||||
*/
|
*/
|
||||||
promptForEula(requireUserAction?: boolean): Promise<boolean>
|
promptForEula(requireUserAction?: boolean): Promise<boolean>;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
extensions/azdata/src/typings/refs.d.ts
vendored
1
extensions/azdata/src/typings/refs.d.ts
vendored
@@ -6,3 +6,4 @@
|
|||||||
/// <reference path='../../../../src/sql/azdata.d.ts'/>
|
/// <reference path='../../../../src/sql/azdata.d.ts'/>
|
||||||
/// <reference path='../../../../src/sql/azdata.proposed.d.ts'/>
|
/// <reference path='../../../../src/sql/azdata.proposed.d.ts'/>
|
||||||
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||||
|
/// <reference path='../../../resource-deployment/src/typings/resource-deployment.d.ts'/>
|
||||||
|
|||||||
14
extensions/resource-deployment/src/api.ts
Normal file
14
extensions/resource-deployment/src/api.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as rd from 'resource-deployment';
|
||||||
|
import { optionsSourcesService } from './services/optionSourcesService';
|
||||||
|
|
||||||
|
export function getExtensionApi(): rd.IExtension {
|
||||||
|
return {
|
||||||
|
registerOptionsSourceProvider: (provider: rd.IOptionsSourceProvider) => optionsSourcesService.registerOptionsSourceProvider(provider)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@@ -3,8 +3,8 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { ToolsInstallPath } from './constants';
|
import { ToolsInstallPath } from '../constants';
|
||||||
import { ITool, NoteBookEnvironmentVariablePrefix } from './interfaces';
|
import { ITool, NoteBookEnvironmentVariablePrefix } from '../interfaces';
|
||||||
|
|
||||||
export function getErrorMessage(error: any): string {
|
export function getErrorMessage(error: any): string {
|
||||||
return (error instanceof Error)
|
return (error instanceof Error)
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import * as arc from 'arc';
|
|
||||||
import { CategoryValue } from 'azdata';
|
|
||||||
import { IOptionsSource } from '../interfaces';
|
|
||||||
import * as loc from '../localizedConstants';
|
|
||||||
import { apiService } from '../services/apiService';
|
|
||||||
import { throwUnless } from '../utils';
|
|
||||||
import { CacheManager } from './cacheManager';
|
|
||||||
|
|
||||||
export const enum OptionsSourceType {
|
|
||||||
ArcControllersOptionsSource = 'ArcControllersOptionsSource',
|
|
||||||
ArcControllerConfigProfilesOptionsSource = 'ArcControllerConfigProfilesOptionsSource'
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class OptionsSource implements IOptionsSource {
|
|
||||||
|
|
||||||
get type(): OptionsSourceType { return this._type; }
|
|
||||||
get variableNames(): { [index: string]: string; } { return this._variableNames; }
|
|
||||||
|
|
||||||
abstract getOptions(): Promise<string[] | CategoryValue[]>;
|
|
||||||
getVariableValue(variableName: string, controllerLabel: string): Promise<string> {
|
|
||||||
throw new Error(loc.variableValueFetchForUnsupportedVariable(variableName));
|
|
||||||
}
|
|
||||||
|
|
||||||
getIsPassword(variableName: string): boolean {
|
|
||||||
throw new Error(loc.isPasswordFetchForUnsupportedVariable(variableName));
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(private _variableNames: { [index: string]: string }, private _type: OptionsSourceType) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that provides options sources for an Arc Data Controller
|
|
||||||
*/
|
|
||||||
export class ArcControllersOptionsSource extends OptionsSource {
|
|
||||||
private _cacheManager = new CacheManager<string, string>();
|
|
||||||
|
|
||||||
async getOptions(): Promise<string[] | CategoryValue[]> {
|
|
||||||
const controllers = await apiService.arcApi.getRegisteredDataControllers();
|
|
||||||
throwUnless(controllers !== undefined && controllers.length !== 0, loc.noControllersConnected);
|
|
||||||
return controllers.map(ci => {
|
|
||||||
return ci.label;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getVariableValue(variableName: string, controllerLabel: string): Promise<string> {
|
|
||||||
const retrieveVariable = async (key: string) => {
|
|
||||||
const [variableName, controllerLabel] = JSON.parse(key);
|
|
||||||
const controllers = await apiService.arcApi.getRegisteredDataControllers();
|
|
||||||
const controller = controllers!.find(ci => ci.label === controllerLabel);
|
|
||||||
throwUnless(controller !== undefined, loc.noControllerInfoFound(controllerLabel));
|
|
||||||
switch (variableName) {
|
|
||||||
case 'endpoint':
|
|
||||||
return controller.info.url;
|
|
||||||
case 'username':
|
|
||||||
return controller.info.username;
|
|
||||||
case 'password':
|
|
||||||
const fetchedPassword = await this.getPassword(controller);
|
|
||||||
return fetchedPassword;
|
|
||||||
default:
|
|
||||||
throw new Error(loc.variableValueFetchForUnsupportedVariable(variableName));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const variableValue = await this._cacheManager.getCacheEntry(JSON.stringify([variableName, controllerLabel]), retrieveVariable);
|
|
||||||
return variableValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getPassword(controller: arc.DataController): Promise<string> {
|
|
||||||
let password = await apiService.arcApi.getControllerPassword(controller.info);
|
|
||||||
if (!password) {
|
|
||||||
password = await apiService.arcApi.reacquireControllerPassword(controller.info, password);
|
|
||||||
}
|
|
||||||
throwUnless(password !== undefined, loc.noPasswordFound(controller.label));
|
|
||||||
return password;
|
|
||||||
}
|
|
||||||
|
|
||||||
getIsPassword(variableName: string): boolean {
|
|
||||||
switch (variableName) {
|
|
||||||
case 'endpoint':
|
|
||||||
case 'username':
|
|
||||||
return false;
|
|
||||||
case 'password':
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
throw new Error(loc.isPasswordFetchForUnsupportedVariable(variableName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that provides options sources for an Arc Data Controller's Config Profiles
|
|
||||||
*/
|
|
||||||
export class ArcControllerConfigProfilesOptionsSource extends OptionsSource {
|
|
||||||
async getOptions(): Promise<string[]> {
|
|
||||||
return (await apiService.azdataApi.azdata.arc.dc.config.list()).result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deferred promise
|
|
||||||
*/
|
|
||||||
export class Deferred<T> {
|
|
||||||
promise: Promise<T>;
|
|
||||||
resolve!: (value?: T | PromiseLike<T>) => void;
|
|
||||||
reject!: (reason?: any) => void;
|
|
||||||
constructor() {
|
|
||||||
this.promise = new Promise<T>((resolve, reject) => {
|
|
||||||
this.resolve = resolve;
|
|
||||||
this.reject = reject;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
then<TResult>(onfulfilled?: (value: T) => TResult | Thenable<TResult>, onrejected?: (reason: any) => TResult | Thenable<TResult>): Thenable<TResult>;
|
|
||||||
then<TResult>(onfulfilled?: (value: T) => TResult | Thenable<TResult>, onrejected?: (reason: any) => void): Thenable<TResult>;
|
|
||||||
then<TResult>(onfulfilled?: (value: T) => TResult | Thenable<TResult>, onrejected?: (reason: any) => TResult | Thenable<TResult>): Thenable<TResult> {
|
|
||||||
return this.promise.then(onfulfilled, onrejected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
|
import { IOptionsSourceProvider } from 'resource-deployment';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { OptionsSourceType } from './helpers/optionSources';
|
|
||||||
|
|
||||||
export const NoteBookEnvironmentVariablePrefix = 'AZDATA_NB_VAR_';
|
export const NoteBookEnvironmentVariablePrefix = 'AZDATA_NB_VAR_';
|
||||||
|
|
||||||
@@ -219,11 +219,9 @@ export type ComponentCSSStyles = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export interface IOptionsSource {
|
export interface IOptionsSource {
|
||||||
readonly type: OptionsSourceType;
|
provider?: IOptionsSourceProvider
|
||||||
readonly variableNames: { [index: string]: string; };
|
readonly variableNames?: { [index: string]: string; };
|
||||||
getOptions(): Promise<string[] | azdata.CategoryValue[]>;
|
readonly providerId: string;
|
||||||
getVariableValue(variableName: string, input: string): Promise<string>;
|
|
||||||
getIsPassword(variableName: string): boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { OptionsSourceType } from './helpers/optionSources';
|
|
||||||
import { FieldType, OptionsType } from './interfaces';
|
import { FieldType, OptionsType } from './interfaces';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
@@ -23,14 +22,11 @@ export const refresh = localize('azure.refresh', "Refresh");
|
|||||||
export const createNewResourceGroup = localize('azure.resourceGroup.createNewResourceGroup', "Create a new resource group");
|
export const createNewResourceGroup = localize('azure.resourceGroup.createNewResourceGroup', "Create a new resource group");
|
||||||
export const NewResourceGroupAriaLabel = localize('azure.resourceGroup.NewResourceGroupAriaLabel', "New resource group name");
|
export const NewResourceGroupAriaLabel = localize('azure.resourceGroup.NewResourceGroupAriaLabel', "New resource group name");
|
||||||
export const realm = localize('deployCluster.Realm', "Realm");
|
export const realm = localize('deployCluster.Realm', "Realm");
|
||||||
export const unexpectedOptionsSourceType = (type: OptionsSourceType) => localize('optionsSourceType.Invalid', "Invalid options source type:{0}", type);
|
|
||||||
export const unknownFieldTypeError = (type: FieldType) => localize('UnknownFieldTypeError', "Unknown field type: \"{0}\"", type);
|
export const unknownFieldTypeError = (type: FieldType) => localize('UnknownFieldTypeError', "Unknown field type: \"{0}\"", type);
|
||||||
|
export const optionsSourceAlreadyDefined = (optionsSourceId: string) => localize('optionsSource.alreadyDefined', "Options Source with id:{0} is already defined", optionsSourceId);
|
||||||
|
export const noOptionsSourceDefined = (optionsSourceId: string) => localize('optionsSource.notDefined', "No Options Source defined for id: {0}", optionsSourceId);
|
||||||
export const variableValueFetchForUnsupportedVariable = (variableName: string) => localize('getVariableValue.unknownVariableName', "Attempt to get variable value for unknown variable:{0}", variableName);
|
export const variableValueFetchForUnsupportedVariable = (variableName: string) => localize('getVariableValue.unknownVariableName', "Attempt to get variable value for unknown variable:{0}", variableName);
|
||||||
export const isPasswordFetchForUnsupportedVariable = (variableName: string) => localize('getIsPassword.unknownVariableName', "Attempt to get isPassword for unknown variable:{0}", variableName);
|
export const isPasswordFetchForUnsupportedVariable = (variableName: string) => localize('getIsPassword.unknownVariableName', "Attempt to get isPassword for unknown variable:{0}", variableName);
|
||||||
export const noControllersConnected = localize('noControllersConnected', "No Azure Arc controllers are currently connected. Please run the command: 'Connect to Existing Azure Arc Controller' and then try again");
|
|
||||||
export const noOptionsSourceDefined = (optionsSourceType: string) => localize('noOptionsSourceDefined', "No OptionsSource defined for type: {0}", optionsSourceType);
|
|
||||||
export const noControllerInfoFound = (name: string) => localize('noControllerInfoFound', "controllerInfo could not be found with name: {0}", name);
|
|
||||||
export const noPasswordFound = (controllerName: string) => localize('noPasswordFound', "Password could not be retrieved for controller: {0} and user did not provide a password. Please retry later.", controllerName);
|
|
||||||
export const optionsNotDefined = (fieldType: FieldType) => localize('optionsNotDefined', "FieldInfo.options was not defined for field type: {0}", fieldType);
|
export const optionsNotDefined = (fieldType: FieldType) => localize('optionsNotDefined', "FieldInfo.options was not defined for field type: {0}", fieldType);
|
||||||
export const optionsNotObjectOrArray = localize('optionsNotObjectOrArray', "FieldInfo.options must be an object if it is not an array");
|
export const optionsNotObjectOrArray = localize('optionsNotObjectOrArray', "FieldInfo.options must be an object if it is not an array");
|
||||||
export const optionsTypeNotFound = localize('optionsTypeNotFound', "When FieldInfo.options is an object it must have 'optionsType' property");
|
export const optionsTypeNotFound = localize('optionsTypeNotFound', "When FieldInfo.options is an object it must have 'optionsType' property");
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ import { ResourceTypeService } from './services/resourceTypeService';
|
|||||||
import { ToolsService } from './services/toolsService';
|
import { ToolsService } from './services/toolsService';
|
||||||
import { DeploymentInputDialog } from './ui/deploymentInputDialog';
|
import { DeploymentInputDialog } from './ui/deploymentInputDialog';
|
||||||
import { ResourceTypePickerDialog } from './ui/resourceTypePickerDialog';
|
import { ResourceTypePickerDialog } from './ui/resourceTypePickerDialog';
|
||||||
|
import * as rd from 'resource-deployment';
|
||||||
|
import { getExtensionApi } from './api';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
export async function activate(context: vscode.ExtensionContext): Promise<void> {
|
export async function activate(context: vscode.ExtensionContext): Promise<rd.IExtension> {
|
||||||
const platformService = new PlatformService(context.globalStoragePath);
|
const platformService = new PlatformService(context.globalStoragePath);
|
||||||
await platformService.initialize();
|
await platformService.initialize();
|
||||||
const toolsService = new ToolsService(platformService);
|
const toolsService = new ToolsService(platformService);
|
||||||
@@ -27,7 +29,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
|
|||||||
const errorMessage = localize('resourceDeployment.FailedToLoadExtension', "Failed to load extension: {0}, Error detected in the resource type definition in package.json, check debug console for details.", context.extensionPath);
|
const errorMessage = localize('resourceDeployment.FailedToLoadExtension', "Failed to load extension: {0}, Error detected in the resource type definition in package.json, check debug console for details.", context.extensionPath);
|
||||||
vscode.window.showErrorMessage(errorMessage);
|
vscode.window.showErrorMessage(errorMessage);
|
||||||
validationFailures.forEach(message => console.error(message));
|
validationFailures.forEach(message => console.error(message));
|
||||||
return;
|
return <any>undefined;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Opens a new ResourceTypePickerDialog
|
* Opens a new ResourceTypePickerDialog
|
||||||
@@ -73,6 +75,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
|
|||||||
const dialog = new DeploymentInputDialog(notebookService, platformService, toolsService, dialogInfo);
|
const dialog = new DeploymentInputDialog(notebookService, platformService, toolsService, dialogInfo);
|
||||||
dialog.open();
|
dialog.open();
|
||||||
});
|
});
|
||||||
|
return getExtensionApi();
|
||||||
}
|
}
|
||||||
|
|
||||||
// this method is called when your extension is deactivated
|
// this method is called when your extension is deactivated
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { isString } from 'util';
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { NotebookPathInfo } from '../interfaces';
|
import { NotebookPathInfo } from '../interfaces';
|
||||||
import { getDateTimeString, getErrorMessage } from '../utils';
|
import { getDateTimeString, getErrorMessage } from '../common/utils';
|
||||||
import { IPlatformService } from './platformService';
|
import { IPlatformService } from './platformService';
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as rd from 'resource-deployment';
|
||||||
|
import * as loc from '../localizedConstants';
|
||||||
|
|
||||||
|
class OptionsSourcesService {
|
||||||
|
private _optionsSourceStore = new Map<string, rd.IOptionsSourceProvider>();
|
||||||
|
registerOptionsSourceProvider(provider: rd.IOptionsSourceProvider): void {
|
||||||
|
if (this._optionsSourceStore.has(provider.optionsSourceId)) {
|
||||||
|
throw new Error(loc.optionsSourceAlreadyDefined(provider.optionsSourceId));
|
||||||
|
}
|
||||||
|
this._optionsSourceStore.set(provider.optionsSourceId, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
getOptionsSource(optionsSourceProviderId: string): rd.IOptionsSourceProvider {
|
||||||
|
const optionsSource = this._optionsSourceStore.get(optionsSourceProviderId);
|
||||||
|
if (optionsSource === undefined) {
|
||||||
|
throw new Error(loc.noOptionsSourceDefined(optionsSourceProviderId));
|
||||||
|
}
|
||||||
|
return optionsSource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const optionsSourcesService = new OptionsSourcesService();
|
||||||
@@ -10,7 +10,7 @@ import * as sudo from 'sudo-prompt';
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { OsDistribution, OsRelease } from '../interfaces';
|
import { OsDistribution, OsRelease } from '../interfaces';
|
||||||
import { getErrorMessage } from '../utils';
|
import { getErrorMessage } from '../common/utils';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
const extensionOutputChannel = localize('resourceDeployment.outputChannel', "Deployments");
|
const extensionOutputChannel = localize('resourceDeployment.outputChannel', "Deployments");
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
import * as azdataExt from 'azdata-ext';
|
||||||
import { EOL } from 'os';
|
import { EOL } from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { SemVer } from 'semver';
|
import { SemVer } from 'semver';
|
||||||
@@ -9,10 +10,9 @@ import * as vscode from 'vscode';
|
|||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { AzdataInstallLocationKey, DeploymentConfigurationKey } from '../../constants';
|
import { AzdataInstallLocationKey, DeploymentConfigurationKey } from '../../constants';
|
||||||
import { Command, OsDistribution, ToolStatus, ToolType } from '../../interfaces';
|
import { Command, OsDistribution, ToolStatus, ToolType } from '../../interfaces';
|
||||||
import { apiService } from '../apiService';
|
import * as loc from '../../localizedConstants';
|
||||||
import { IPlatformService } from '../platformService';
|
import { IPlatformService } from '../platformService';
|
||||||
import { dependencyType, ToolBase } from './toolBase';
|
import { dependencyType, ToolBase } from './toolBase';
|
||||||
import * as loc from '../../localizedConstants';
|
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
export const AzdataToolName = 'azdata';
|
export const AzdataToolName = 'azdata';
|
||||||
@@ -21,6 +21,7 @@ const macInstallationRoot = '/usr/local/bin';
|
|||||||
const debianInstallationRoot = '/usr/local/bin';
|
const debianInstallationRoot = '/usr/local/bin';
|
||||||
|
|
||||||
export class AzdataTool extends ToolBase {
|
export class AzdataTool extends ToolBase {
|
||||||
|
private azdataApi!: azdataExt.IExtension;
|
||||||
constructor(platformService: IPlatformService) {
|
constructor(platformService: IPlatformService) {
|
||||||
super(platformService);
|
super(platformService);
|
||||||
}
|
}
|
||||||
@@ -46,7 +47,7 @@ export class AzdataTool extends ToolBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public isEulaAccepted(): boolean {
|
public isEulaAccepted(): boolean {
|
||||||
if (apiService.azdataApi.isEulaAccepted()) {
|
if (this.azdataApi.isEulaAccepted()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
this.setStatusDescription(loc.azdataEulaNotAccepted);
|
this.setStatusDescription(loc.azdataEulaNotAccepted);
|
||||||
@@ -55,7 +56,7 @@ export class AzdataTool extends ToolBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async promptForEula(): Promise<boolean> {
|
public async promptForEula(): Promise<boolean> {
|
||||||
const eulaAccepted = await apiService.azdataApi.promptForEula();
|
const eulaAccepted = await this.azdataApi.promptForEula();
|
||||||
if (!eulaAccepted) {
|
if (!eulaAccepted) {
|
||||||
this.setStatusDescription(loc.azdataEulaDeclined);
|
this.setStatusDescription(loc.azdataEulaDeclined);
|
||||||
}
|
}
|
||||||
@@ -80,15 +81,16 @@ export class AzdataTool extends ToolBase {
|
|||||||
* updates the version and status for the tool.
|
* updates the version and status for the tool.
|
||||||
*/
|
*/
|
||||||
protected async updateVersionAndStatus(): Promise<void> {
|
protected async updateVersionAndStatus(): Promise<void> {
|
||||||
|
this.azdataApi = await vscode.extensions.getExtension(azdataExt.extension.name)?.activate();
|
||||||
this.setStatusDescription('');
|
this.setStatusDescription('');
|
||||||
await this.addInstallationSearchPathsToSystemPath();
|
await this.addInstallationSearchPathsToSystemPath();
|
||||||
|
|
||||||
const commandOutput = await apiService.azdataApi.azdata.version();
|
const commandOutput = await this.azdataApi.azdata.version();
|
||||||
this.version = apiService.azdataApi.azdata.getSemVersion();
|
this.version = await this.azdataApi.azdata.getSemVersion();
|
||||||
if (this.version) {
|
if (this.version) {
|
||||||
if (this.autoInstallSupported) {
|
if (this.autoInstallSupported) {
|
||||||
// set the installationPath
|
// set the installationPath
|
||||||
this.setInstallationPathOrAdditionalInformation(apiService.azdataApi.azdata.getPath());
|
this.setInstallationPathOrAdditionalInformation(await this.azdataApi.azdata.getPath());
|
||||||
}
|
}
|
||||||
this.setStatus(ToolStatus.Installed);
|
this.setStatus(ToolStatus.Installed);
|
||||||
}
|
}
|
||||||
@@ -99,8 +101,8 @@ export class AzdataTool extends ToolBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getVersionFromOutput(output: string): SemVer | undefined {
|
protected getVersionFromOutput(output: string): SemVer | Promise<SemVer> | undefined {
|
||||||
return apiService.azdataApi.azdata.getSemVersion();
|
return this.azdataApi.azdata.getSemVersion();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import { EOL } from 'os';
|
import { EOL } from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { SemVer, compare as SemVerCompare } from 'semver';
|
import { compare as SemVerCompare, SemVer } from 'semver';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
|
import { getErrorMessage } from '../../common/utils';
|
||||||
import { Command, ITool, OsDistribution, ToolStatus, ToolType } from '../../interfaces';
|
import { Command, ITool, OsDistribution, ToolStatus, ToolType } from '../../interfaces';
|
||||||
import { getErrorMessage } from '../../utils';
|
|
||||||
import { IPlatformService } from '../platformService';
|
import { IPlatformService } from '../platformService';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
@@ -52,7 +52,7 @@ export abstract class ToolBase implements ITool {
|
|||||||
protected abstract readonly allInstallationCommands: Map<OsDistribution, Command[]>;
|
protected abstract readonly allInstallationCommands: Map<OsDistribution, Command[]>;
|
||||||
protected readonly dependenciesByOsType: Map<OsDistribution, dependencyType[]> = new Map<OsDistribution, dependencyType[]>();
|
protected readonly dependenciesByOsType: Map<OsDistribution, dependencyType[]> = new Map<OsDistribution, dependencyType[]>();
|
||||||
|
|
||||||
protected abstract getVersionFromOutput(output: string): SemVer | undefined;
|
protected abstract getVersionFromOutput(output: string): SemVer | Promise<SemVer> | undefined;
|
||||||
protected readonly _onDidUpdateData = new vscode.EventEmitter<ITool>();
|
protected readonly _onDidUpdateData = new vscode.EventEmitter<ITool>();
|
||||||
protected readonly uninstallCommand?: string;
|
protected readonly uninstallCommand?: string;
|
||||||
|
|
||||||
@@ -274,7 +274,7 @@ export abstract class ToolBase implements ITool {
|
|||||||
ignoreError: true
|
ignoreError: true
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
this.version = this.getVersionFromOutput(commandOutput);
|
this.version = await this.getVersionFromOutput(commandOutput);
|
||||||
if (this.version) {
|
if (this.version) {
|
||||||
if (this.autoInstallSupported) {
|
if (this.autoInstallSupported) {
|
||||||
// discover and set the installationPath
|
// discover and set the installationPath
|
||||||
|
|||||||
28
extensions/resource-deployment/src/typings/resource-deployment.d.ts
vendored
Normal file
28
extensions/resource-deployment/src/typings/resource-deployment.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
declare module 'resource-deployment' {
|
||||||
|
import * as azdata from 'azdata';
|
||||||
|
|
||||||
|
export const enum extension {
|
||||||
|
name = 'Microsoft.resource-deployment'
|
||||||
|
}
|
||||||
|
export interface IOptionsSourceProvider {
|
||||||
|
readonly optionsSourceId: string,
|
||||||
|
getOptions(): Promise<string[] | azdata.CategoryValue[]> | string[] | azdata.CategoryValue[];
|
||||||
|
getVariableValue?: (variableName: string, input: string) => Promise<string> | string;
|
||||||
|
getIsPassword?: (variableName: string) => boolean | Promise<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Covers defining what the resource-deployment extension exports to other extensions
|
||||||
|
*
|
||||||
|
* IMPORTANT: THIS IS NOT A HARD DEFINITION unlike vscode; therefore no enums or classes should be defined here
|
||||||
|
* (const enums get evaluated when typescript -> javascript so those are fine)
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface IExtension {
|
||||||
|
registerOptionsSourceProvider(provider: IOptionsSourceProvider): void
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ import { IAzdataService } from '../../services/azdataService';
|
|||||||
import { IKubeService } from '../../services/kubeService';
|
import { IKubeService } from '../../services/kubeService';
|
||||||
import { INotebookService } from '../../services/notebookService';
|
import { INotebookService } from '../../services/notebookService';
|
||||||
import { IToolsService } from '../../services/toolsService';
|
import { IToolsService } from '../../services/toolsService';
|
||||||
import { getErrorMessage } from '../../utils';
|
import { getErrorMessage } from '../../common/utils';
|
||||||
import { InputComponents } from '../modelViewUtils';
|
import { InputComponents } from '../modelViewUtils';
|
||||||
import { WizardBase } from '../wizardBase';
|
import { WizardBase } from '../wizardBase';
|
||||||
import { WizardPageBase } from '../wizardPageBase';
|
import { WizardPageBase } from '../wizardPageBase';
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { delimiter } from 'path';
|
|||||||
import { BdcDeploymentType, ITool } from '../../interfaces';
|
import { BdcDeploymentType, ITool } from '../../interfaces';
|
||||||
import { BigDataClusterDeploymentProfile, DataResource, HdfsResource, SqlServerMasterResource } from '../../services/bigDataClusterDeploymentProfile';
|
import { BigDataClusterDeploymentProfile, DataResource, HdfsResource, SqlServerMasterResource } from '../../services/bigDataClusterDeploymentProfile';
|
||||||
import { KubeCtlToolName } from '../../services/tools/kubeCtlTool';
|
import { KubeCtlToolName } from '../../services/tools/kubeCtlTool';
|
||||||
import { getRuntimeBinaryPathEnvironmentVariableName, setEnvironmentVariablesForInstallPaths } from '../../utils';
|
import { getRuntimeBinaryPathEnvironmentVariableName, setEnvironmentVariablesForInstallPaths } from '../../common/utils';
|
||||||
import { Model } from '../model';
|
import { Model } from '../model';
|
||||||
import { ToolsInstallPath } from './../../constants';
|
import { ToolsInstallPath } from './../../constants';
|
||||||
import * as VariableNames from './constants';
|
import * as VariableNames from './constants';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import { EOL } from 'os';
|
import { EOL } from 'os';
|
||||||
import { ITool, NoteBookEnvironmentVariablePrefix } from '../interfaces';
|
import { ITool, NoteBookEnvironmentVariablePrefix } from '../interfaces';
|
||||||
import { setEnvironmentVariablesForInstallPaths, getRuntimeBinaryPathEnvironmentVariableName } from '../utils';
|
import { setEnvironmentVariablesForInstallPaths, getRuntimeBinaryPathEnvironmentVariableName } from '../common/utils';
|
||||||
import { ToolsInstallPath } from '../constants';
|
import { ToolsInstallPath } from '../constants';
|
||||||
import { delimiter } from 'path';
|
import { delimiter } from 'path';
|
||||||
|
|
||||||
|
|||||||
@@ -9,17 +9,18 @@ import { EOL, homedir as os_homedir } from 'os';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { ArcControllerConfigProfilesOptionsSource, ArcControllersOptionsSource, OptionsSourceType } from '../helpers/optionSources';
|
|
||||||
import { AzureAccountFieldInfo, AzureLocationsFieldInfo, ComponentCSSStyles, DialogInfoBase, FieldInfo, FieldType, FilePickerFieldInfo, IOptionsSource, KubeClusterContextFieldInfo, LabelPosition, NoteBookEnvironmentVariablePrefix, OptionsInfo, OptionsType, PageInfoBase, RowInfo, SectionInfo, TextCSSStyles } from '../interfaces';
|
import { AzureAccountFieldInfo, AzureLocationsFieldInfo, ComponentCSSStyles, DialogInfoBase, FieldInfo, FieldType, FilePickerFieldInfo, IOptionsSource, KubeClusterContextFieldInfo, LabelPosition, NoteBookEnvironmentVariablePrefix, OptionsInfo, OptionsType, PageInfoBase, RowInfo, SectionInfo, TextCSSStyles } from '../interfaces';
|
||||||
import * as loc from '../localizedConstants';
|
import * as loc from '../localizedConstants';
|
||||||
import { apiService } from '../services/apiService';
|
import { apiService } from '../services/apiService';
|
||||||
import { getDefaultKubeConfigPath, getKubeConfigClusterContexts } from '../services/kubeService';
|
import { getDefaultKubeConfigPath, getKubeConfigClusterContexts } from '../services/kubeService';
|
||||||
import { KubeCtlTool, KubeCtlToolName } from '../services/tools/kubeCtlTool';
|
import { KubeCtlTool, KubeCtlToolName } from '../services/tools/kubeCtlTool';
|
||||||
import { IToolsService } from '../services/toolsService';
|
import { IToolsService } from '../services/toolsService';
|
||||||
import { getDateTimeString, getErrorMessage, throwUnless } from '../utils';
|
import { getDateTimeString, getErrorMessage, throwUnless } from '../common/utils';
|
||||||
import { WizardInfoBase } from './../interfaces';
|
import { WizardInfoBase } from './../interfaces';
|
||||||
import { Model } from './model';
|
import { Model } from './model';
|
||||||
import { RadioGroupLoadingComponentBuilder } from './radioGroupLoadingComponentBuilder';
|
import { RadioGroupLoadingComponentBuilder } from './radioGroupLoadingComponentBuilder';
|
||||||
|
import { optionsSourcesService } from '../services/optionSourcesService';
|
||||||
|
import { IOptionsSourceProvider } from 'resource-deployment';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
@@ -432,21 +433,10 @@ async function processOptionsTypeField(context: FieldContext): Promise<void> {
|
|||||||
}
|
}
|
||||||
throwUnless(typeof context.fieldInfo.options === 'object', loc.optionsNotObjectOrArray);
|
throwUnless(typeof context.fieldInfo.options === 'object', loc.optionsNotObjectOrArray);
|
||||||
throwUnless('optionsType' in context.fieldInfo.options, loc.optionsTypeNotFound);
|
throwUnless('optionsType' in context.fieldInfo.options, loc.optionsTypeNotFound);
|
||||||
if (context.fieldInfo.options.source) {
|
if (context.fieldInfo.options.source?.providerId) {
|
||||||
try {
|
try {
|
||||||
let optionsSource: IOptionsSource;
|
context.fieldInfo.options.source.provider = optionsSourcesService.getOptionsSource(context.fieldInfo.options.source.providerId);
|
||||||
switch (context.fieldInfo.options.source.type) {
|
context.fieldInfo.options.values = await context.fieldInfo.options.source.provider.getOptions();
|
||||||
case OptionsSourceType.ArcControllersOptionsSource:
|
|
||||||
optionsSource = new ArcControllersOptionsSource(context.fieldInfo.options.source.variableNames, context.fieldInfo.options.source.type);
|
|
||||||
break;
|
|
||||||
case OptionsSourceType.ArcControllerConfigProfilesOptionsSource:
|
|
||||||
optionsSource = new ArcControllerConfigProfilesOptionsSource(context.fieldInfo.options.source.variableNames, context.fieldInfo.options.source.type);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(loc.noOptionsSourceDefined(context.fieldInfo.options.source.type));
|
|
||||||
}
|
|
||||||
context.fieldInfo.options.source = optionsSource;
|
|
||||||
context.fieldInfo.options.values = await context.fieldInfo.options.source.getOptions();
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
disableControlButtons(context.container);
|
disableControlButtons(context.container);
|
||||||
@@ -466,35 +456,39 @@ async function processOptionsTypeField(context: FieldContext): Promise<void> {
|
|||||||
throwUnless(context.fieldInfo.options.optionsType === OptionsType.Dropdown, loc.optionsTypeRadioOrDropdown);
|
throwUnless(context.fieldInfo.options.optionsType === OptionsType.Dropdown, loc.optionsTypeRadioOrDropdown);
|
||||||
optionsComponent = processDropdownOptionsTypeField(context);
|
optionsComponent = processDropdownOptionsTypeField(context);
|
||||||
}
|
}
|
||||||
if (context.fieldInfo.options.source) {
|
const optionsSource = context.fieldInfo.options.source;
|
||||||
const optionsSource = context.fieldInfo.options.source;
|
if (optionsSource?.provider) {
|
||||||
for (const key of Object.keys(optionsSource.variableNames ?? {})) {
|
const optionsSourceProvider = optionsSource.provider;
|
||||||
context.fieldInfo.subFields!.push({
|
await Promise.all(Object.keys(context.fieldInfo.options.source?.variableNames ?? {}).map(async key => {
|
||||||
label: context.fieldInfo.label,
|
await configureOptionsSourceSubfields(context, optionsSource, key, optionsComponent, optionsSourceProvider);
|
||||||
variableName: optionsSource.variableNames[key]
|
}));
|
||||||
});
|
|
||||||
context.onNewInputComponentCreated(optionsSource.variableNames[key], {
|
|
||||||
component: optionsComponent,
|
|
||||||
inputValueTransformer: async (controllerName: string) => {
|
|
||||||
try {
|
|
||||||
const variableValue = await optionsSource.getVariableValue(key, controllerName);
|
|
||||||
return variableValue;
|
|
||||||
} catch (e) {
|
|
||||||
disableControlButtons(context.container);
|
|
||||||
context.container.message = {
|
|
||||||
text: getErrorMessage(e),
|
|
||||||
description: '',
|
|
||||||
level: azdata.window.MessageLevel.Error
|
|
||||||
};
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isPassword: optionsSource.getIsPassword(key)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function configureOptionsSourceSubfields(context: FieldContext, optionsSource: IOptionsSource, variableKey: string, optionsComponent: InputComponent, optionsSourceProvider: IOptionsSourceProvider) {
|
||||||
|
context.fieldInfo.subFields!.push({
|
||||||
|
label: context.fieldInfo.label,
|
||||||
|
variableName: optionsSource.variableNames![variableKey]
|
||||||
|
});
|
||||||
|
context.onNewInputComponentCreated(optionsSource.variableNames![variableKey], {
|
||||||
|
component: optionsComponent,
|
||||||
|
inputValueTransformer: async (optionValue: string) => {
|
||||||
|
try {
|
||||||
|
return await optionsSourceProvider.getVariableValue!(variableKey, optionValue);
|
||||||
|
} catch (e) {
|
||||||
|
disableControlButtons(context.container);
|
||||||
|
context.container.message = {
|
||||||
|
text: getErrorMessage(e),
|
||||||
|
description: '',
|
||||||
|
level: azdata.window.MessageLevel.Error
|
||||||
|
};
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isPassword: await optionsSourceProvider.getIsPassword!(variableKey)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function processDropdownOptionsTypeField(context: FieldContext): azdata.DropDownComponent {
|
function processDropdownOptionsTypeField(context: FieldContext): azdata.DropDownComponent {
|
||||||
const label = createLabel(context.view, { text: context.fieldInfo.label, description: context.fieldInfo.description, required: context.fieldInfo.required, width: context.fieldInfo.labelWidth, cssStyles: context.fieldInfo.labelCSSStyles });
|
const label = createLabel(context.view, { text: context.fieldInfo.label, description: context.fieldInfo.description, required: context.fieldInfo.required, width: context.fieldInfo.labelWidth, cssStyles: context.fieldInfo.labelCSSStyles });
|
||||||
const options = context.fieldInfo.options as OptionsInfo;
|
const options = context.fieldInfo.options as OptionsInfo;
|
||||||
@@ -1304,7 +1298,7 @@ async function getInputComponentValue(inputComponents: InputComponents, key: str
|
|||||||
if (input === undefined) {
|
if (input === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
let value;
|
let value: string | undefined;
|
||||||
if (input instanceof RadioGroupLoadingComponentBuilder) {
|
if (input instanceof RadioGroupLoadingComponentBuilder) {
|
||||||
value = input.value;
|
value = input.value;
|
||||||
} else if ('checked' in input) { // CheckBoxComponent
|
} else if ('checked' in input) { // CheckBoxComponent
|
||||||
@@ -1319,14 +1313,7 @@ async function getInputComponentValue(inputComponents: InputComponents, key: str
|
|||||||
} else {
|
} else {
|
||||||
throw new Error(`Unknown input type with ID ${input.id}`);
|
throw new Error(`Unknown input type with ID ${input.id}`);
|
||||||
}
|
}
|
||||||
const inputValueTransformer = inputComponents[key].inputValueTransformer;
|
return inputComponents[key].inputValueTransformer ? await inputComponents[key].inputValueTransformer!(value ?? '') : value;
|
||||||
if (inputValueTransformer) {
|
|
||||||
value = inputValueTransformer(value ?? '');
|
|
||||||
if (typeof value !== 'string') {
|
|
||||||
value = await value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isInputBoxEmpty(input: azdata.InputBoxComponent): boolean {
|
export function isInputBoxEmpty(input: azdata.InputBoxComponent): boolean {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { OptionsInfo, FieldInfo } from '../interfaces';
|
import { OptionsInfo, FieldInfo } from '../interfaces';
|
||||||
import { getErrorMessage } from '../utils';
|
import { getErrorMessage } from '../common/utils';
|
||||||
|
|
||||||
export class RadioGroupLoadingComponentBuilder implements azdata.ComponentBuilder<azdata.LoadingComponent, azdata.LoadingComponentProperties> {
|
export class RadioGroupLoadingComponentBuilder implements azdata.ComponentBuilder<azdata.LoadingComponent, azdata.LoadingComponentProperties> {
|
||||||
private _optionsDivContainer!: azdata.DivContainer;
|
private _optionsDivContainer!: azdata.DivContainer;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { AgreementInfo, DeploymentProvider, ITool, ResourceType, ToolStatus } fr
|
|||||||
import { select } from '../localizedConstants';
|
import { select } from '../localizedConstants';
|
||||||
import { IResourceTypeService } from '../services/resourceTypeService';
|
import { IResourceTypeService } from '../services/resourceTypeService';
|
||||||
import { IToolsService } from '../services/toolsService';
|
import { IToolsService } from '../services/toolsService';
|
||||||
import { getErrorMessage } from '../utils';
|
import { getErrorMessage } from '../common/utils';
|
||||||
import * as loc from './../localizedConstants';
|
import * as loc from './../localizedConstants';
|
||||||
import { DialogBase } from './dialogBase';
|
import { DialogBase } from './dialogBase';
|
||||||
import { createFlexContainer } from './modelViewUtils';
|
import { createFlexContainer } from './modelViewUtils';
|
||||||
|
|||||||
Reference in New Issue
Block a user