Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions l10n/bundle.l10n.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"A connection with the same username and host already exists.": "A connection with the same username and host already exists.",
"A new connection will be added to your Connections View.\nDo you want to continue?\n\nNote: You can disable these URL handling confirmations in the exension settings.": "A new connection will be added to your Connections View.\nDo you want to continue?\n\nNote: You can disable these URL handling confirmations in the exension settings.",
"A value is required to proceed.": "A value is required to proceed.",
"Account information is incomplete.": "Account information is incomplete.",
"Add new document": "Add new document",
"Advanced": "Advanced",
"All available providers have been added already.": "All available providers have been added already.",
Expand All @@ -56,9 +57,11 @@
"Authentication is required to run this action.": "Authentication is required to run this action.",
"Authentication is required to use this migration provider.": "Authentication is required to use this migration provider.",
"Azure Activity": "Azure Activity",
"Azure Cosmos DB for MongoDB (RU)": "Azure Cosmos DB for MongoDB (RU)",
"Azure Cosmos DB for MongoDB (RU) Emulator": "Azure Cosmos DB for MongoDB (RU) Emulator",
"Azure Cosmos DB for MongoDB (vCore)": "Azure Cosmos DB for MongoDB (vCore)",
"Azure Service Discovery": "Azure Service Discovery",
"Azure Service Discovery for MongoDB RU": "Azure Service Discovery for MongoDB RU",
"Azure VM Service Discovery": "Azure VM Service Discovery",
"Azure VM: Attempting to authenticate with \"{vmName}\"…": "Azure VM: Attempting to authenticate with \"{vmName}\"…",
"Azure VM: Connected to \"{vmName}\" as \"{username}\".": "Azure VM: Connected to \"{vmName}\" as \"{username}\".",
Expand All @@ -70,6 +73,7 @@
"Change page size": "Change page size",
"Check document syntax": "Check document syntax",
"Choose a cluster…": "Choose a cluster…",
"Choose a RU cluster…": "Choose a RU cluster…",
"Choose a subscription…": "Choose a subscription…",
"Choose a Virtual Machine…": "Choose a Virtual Machine…",
"Choose the data migration provider…": "Choose the data migration provider…",
Expand Down Expand Up @@ -266,10 +270,12 @@
"Load More...": "Load More...",
"Loading \"{0}\"...": "Loading \"{0}\"...",
"Loading cluster details for \"{cluster}\"": "Loading cluster details for \"{cluster}\"",
"Loading clusters…": "Loading clusters…",
"Loading Content": "Loading Content",
"Loading document {num} of {countUri}": "Loading document {num} of {countUri}",
"Loading documents…": "Loading documents…",
"Loading resources...": "Loading resources...",
"Loading RU clusters…": "Loading RU clusters…",
"Loading subscriptions…": "Loading subscriptions…",
"Loading Virtual Machines…": "Loading Virtual Machines…",
"Loading...": "Loading...",
Expand Down Expand Up @@ -434,6 +440,7 @@
"Unable to connect to the local database instance. Make sure it is started correctly. See {link} for tips.": "Unable to connect to the local database instance. Make sure it is started correctly. See {link} for tips.",
"Unable to connect to the local instance. Make sure it is started correctly. See {link} for tips.": "Unable to connect to the local instance. Make sure it is started correctly. See {link} for tips.",
"Unable to parse syntax near line {line}, col {column}: {message}": "Unable to parse syntax near line {line}, col {column}: {message}",
"Unable to retrieve credentials for cluster \"{cluster}\".": "Unable to retrieve credentials for cluster \"{cluster}\".",
"Unable to retrieve credentials for the selected cluster.": "Unable to retrieve credentials for the selected cluster.",
"Unexpected status code: {0}": "Unexpected status code: {0}",
"Unknown error": "Unknown error",
Expand Down
4 changes: 3 additions & 1 deletion src/documentdb/ClustersExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ import { updateConnectionString } from '../commands/updateConnectionString/updat
import { updateCredentials } from '../commands/updateCredentials/updateCredentials';
import { isVCoreAndRURolloutEnabled } from '../extension';
import { ext } from '../extensionVariables';
import { AzureMongoRUDiscoveryProvider } from '../plugins/service-azure-mongo-ru/AzureMongoRUDiscoveryProvider';
import { AzureDiscoveryProvider } from '../plugins/service-azure-mongo-vcore/AzureDiscoveryProvider';
import { AzureVMDiscoveryProvider } from '../plugins/service-azure-vm/AzureVMDiscoveryProvider';
import { AzureDiscoveryProvider } from '../plugins/service-azure/AzureDiscoveryProvider';
import { DiscoveryService } from '../services/discoveryServices';
import { VCoreBranchDataProvider } from '../tree/azure-resources-view/documentdb/VCoreBranchDataProvider';
import { RUBranchDataProvider } from '../tree/azure-resources-view/mongo-ru/RUBranchDataProvider';
Expand All @@ -70,6 +71,7 @@ export class ClustersExtension implements vscode.Disposable {

registerDiscoveryServices(_activateContext: IActionContext) {
DiscoveryService.registerProvider(new AzureDiscoveryProvider());
DiscoveryService.registerProvider(new AzureMongoRUDiscoveryProvider());
DiscoveryService.registerProvider(new AzureVMDiscoveryProvider());
}

Expand Down
10 changes: 10 additions & 0 deletions src/plugins/api-shared/azure/wizard/AzureContextProperties.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

export enum AzureContextProperties {
AzureSubscriptionProvider = 'azureSubscriptionProvider',
SelectedSubscription = 'selectedSubscription',
SelectedCluster = 'selectedCluster',
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { VSCodeAzureSubscriptionProvider } from '@microsoft/vscode-azext-azureau
import { AzureWizardPromptStep, UserCancelledError } from '@microsoft/vscode-azext-utils';
import * as l10n from '@vscode/l10n';
import { Uri, window, type MessageItem, type QuickPickItem } from 'vscode';
import { type NewConnectionWizardContext } from '../../../commands/newConnection/NewConnectionWizardContext';
import { ext } from '../../../extensionVariables';
import { AzureContextProperties } from '../AzureDiscoveryProvider';
import { type NewConnectionWizardContext } from '../../../../commands/newConnection/NewConnectionWizardContext';
import { ext } from '../../../../extensionVariables';
import { AzureContextProperties } from './AzureContextProperties';

export class SelectSubscriptionStep extends AzureWizardPromptStep<NewConnectionWizardContext> {
iconPath = Uri.joinPath(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { type IActionContext, type IWizardOptions } from '@microsoft/vscode-azext-utils';
import { Disposable, l10n, ThemeIcon } from 'vscode';
import { type NewConnectionWizardContext } from '../../commands/newConnection/NewConnectionWizardContext';
import { ext } from '../../extensionVariables';
import { type DiscoveryProvider } from '../../services/discoveryServices';
import { type TreeElement } from '../../tree/TreeElement';
import { AzureSubscriptionProviderWithFilters } from '../api-shared/azure/AzureSubscriptionProviderWithFilters';
import { configureAzureSubscriptionFilter } from '../api-shared/azure/subscriptionFiltering';
import { AzureContextProperties } from '../api-shared/azure/wizard/AzureContextProperties';
import { SelectSubscriptionStep } from '../service-azure-vm/discovery-wizard/SelectSubscriptionStep';
import { AzureMongoRUServiceRootItem } from './discovery-tree/AzureMongoRUServiceRootItem';
import { AzureMongoRUExecuteStep } from './discovery-wizard/AzureMongoRUExecuteStep';
import { SelectRUClusterStep } from './discovery-wizard/SelectRUClusterStep';

export class AzureMongoRUDiscoveryProvider extends Disposable implements DiscoveryProvider {
id = 'azure-mongo-ru-discovery';
label = l10n.t('Azure Cosmos DB for MongoDB (RU)');
description = l10n.t('Azure Service Discovery for MongoDB RU');
iconPath = new ThemeIcon('azure');

azureSubscriptionProvider: AzureSubscriptionProviderWithFilters;

constructor() {
super(() => {
this.azureSubscriptionProvider.dispose();
});

this.azureSubscriptionProvider = new AzureSubscriptionProviderWithFilters();
}

getDiscoveryTreeRootItem(parentId: string): TreeElement {
return new AzureMongoRUServiceRootItem(this.azureSubscriptionProvider, parentId);
}

getDiscoveryWizard(context: NewConnectionWizardContext): IWizardOptions<NewConnectionWizardContext> {
context.properties[AzureContextProperties.AzureSubscriptionProvider] = this.azureSubscriptionProvider;

return {
title: l10n.t('Azure Service Discovery'),
promptSteps: [new SelectSubscriptionStep(), new SelectRUClusterStep()],
executeSteps: [new AzureMongoRUExecuteStep()],
showLoadingPrompt: true,
};
}

getLearnMoreUrl(): string | undefined {
return 'https://aka.ms/vscode-documentdb-discovery-providers-azure-ru';
}

async configureTreeItemFilter(context: IActionContext, node: TreeElement): Promise<void> {
if (node instanceof AzureMongoRUServiceRootItem) {
await configureAzureSubscriptionFilter(context, this.azureSubscriptionProvider);
ext.discoveryBranchDataProvider.refresh(node);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { type VSCodeAzureSubscriptionProvider } from '@microsoft/vscode-azext-azureauth';
import * as l10n from '@vscode/l10n';
import * as vscode from 'vscode';
import { ext } from '../../../extensionVariables';
import { createGenericElementWithContext } from '../../../tree/api/createGenericElementWithContext';
import { type ExtTreeElementBase, type TreeElement } from '../../../tree/TreeElement';
import {
isTreeElementWithContextValue,
type TreeElementWithContextValue,
} from '../../../tree/TreeElementWithContextValue';
import { type TreeElementWithRetryChildren } from '../../../tree/TreeElementWithRetryChildren';
import { AzureMongoRUSubscriptionItem } from './AzureMongoRUSubscriptionItem';

export class AzureMongoRUServiceRootItem
implements TreeElement, TreeElementWithContextValue, TreeElementWithRetryChildren
{
public readonly id: string;
public contextValue: string = 'enableRefreshCommand;enableFilterCommand;enableLearnMoreCommand;azureMongoRUService';

constructor(
private readonly azureSubscriptionProvider: VSCodeAzureSubscriptionProvider,
public readonly parentId: string,
) {
this.id = `${parentId}/azure-mongo-ru-discovery`;
}

async getChildren(): Promise<ExtTreeElementBase[]> {
/**
* This is an important step to ensure that the user is signed in to Azure before listing subscriptions.
*/
if (!(await this.azureSubscriptionProvider.isSignedIn())) {
const signIn: vscode.MessageItem = { title: l10n.t('Sign In') };
void vscode.window
.showInformationMessage(l10n.t('You are not signed in to Azure. Sign in and retry.'), signIn)
.then(async (input) => {
if (input === signIn) {
await this.azureSubscriptionProvider.signIn();
ext.discoveryBranchDataProvider.refresh();
}
});

return [
createGenericElementWithContext({
contextValue: 'error', // note: keep this in sync with the `hasRetryNode` function in this file
id: `${this.id}/retry`,
label: vscode.l10n.t('Click here to retry'),
iconPath: new vscode.ThemeIcon('refresh'),
commandId: 'vscode-documentdb.command.internal.retry',
commandArgs: [this],
}),
];
}

const subscriptions = await this.azureSubscriptionProvider.getSubscriptions(true);
if (!subscriptions || subscriptions.length === 0) {
return [];
}

return (
subscriptions
// sort by name
.sort((a, b) => a.name.localeCompare(b.name))
// map to AzureMongoRUSubscriptionItem
.map((sub) => {
return new AzureMongoRUSubscriptionItem(this.id, {
subscription: sub,
subscriptionName: sub.name,
subscriptionId: sub.subscriptionId,
});
})
);
}

public hasRetryNode(children: TreeElement[] | null | undefined): boolean {
return (
children?.some((child) => isTreeElementWithContextValue(child) && child.contextValue === 'error') ?? false
);
}

public getTreeItem(): vscode.TreeItem {
return {
id: this.id,
contextValue: this.contextValue,
label: l10n.t('Azure Cosmos DB for MongoDB (RU)'),
iconPath: new vscode.ThemeIcon('azure'),
collapsibleState: vscode.TreeItemCollapsibleState.Collapsed,
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { getResourceGroupFromId, uiUtils } from '@microsoft/vscode-azext-azureutils';
import { callWithTelemetryAndErrorHandling, type IActionContext } from '@microsoft/vscode-azext-utils';
import { type AzureSubscription } from '@microsoft/vscode-azureresources-api';
import * as vscode from 'vscode';
import { CosmosDBMongoRUExperience } from '../../../DocumentDBExperiences';
import { ext } from '../../../extensionVariables';
import { type TreeElement } from '../../../tree/TreeElement';
import { type TreeElementWithContextValue } from '../../../tree/TreeElementWithContextValue';
import { type ClusterModel } from '../../../tree/documentdb/ClusterModel';
import { createCosmosDBManagementClient } from '../../../utils/azureClients';
import { nonNullProp } from '../../../utils/nonNull';
import { MongoRUResourceItem } from './documentdb/MongoRUResourceItem';

export interface AzureSubscriptionModel {
subscriptionName: string;
subscription: AzureSubscription;
subscriptionId: string;
}

export class AzureMongoRUSubscriptionItem implements TreeElement, TreeElementWithContextValue {
public readonly id: string;
public contextValue: string = 'enableRefreshCommand;azureMongoRUSubscription';

constructor(
public readonly parentId: string,
public readonly subscription: AzureSubscriptionModel,
) {
this.id = `${parentId}/${subscription.subscriptionId}`;
}

async getChildren(): Promise<TreeElement[] | null | undefined> {
return await callWithTelemetryAndErrorHandling(
'azure-mongo-ru-discovery.getChildren',
async (context: IActionContext) => {
context.telemetry.properties.discoveryProvider = 'azure-mongo-ru-discovery';

const managementClient = await createCosmosDBManagementClient(context, this.subscription.subscription);
const allAccounts = await uiUtils.listAllIterator(managementClient.databaseAccounts.list());
const accounts = allAccounts.filter((account) => account.kind === 'MongoDB');

return accounts
.sort((a, b) => (a.name || '').localeCompare(b.name || ''))
.map((account) => {
const resourceId = nonNullProp(account, 'id', 'account.id', 'AzureMongoRUSubscriptionItem.ts');

const clusterInfo: ClusterModel = {
...account,
resourceGroup: getResourceGroupFromId(resourceId),
dbExperience: CosmosDBMongoRUExperience,
} as ClusterModel;

return new MongoRUResourceItem(this.subscription.subscription, clusterInfo);
});
},
);
}

public getTreeItem(): vscode.TreeItem {
return {
id: this.id,
contextValue: this.contextValue,
label: this.subscription.subscriptionName,
tooltip: `Subscription ID: ${this.subscription.subscriptionId}`,
iconPath: vscode.Uri.joinPath(
ext.context.extensionUri,
'resources',
'from_node_modules',
'@microsoft',
'vscode-azext-azureutils',
'resources',
'azureSubscription.svg',
),
collapsibleState: vscode.TreeItemCollapsibleState.Collapsed,
};
}
}
Loading